diff --git a/drivers/video/mxc/mxcfb_siihdmi.c b/drivers/video/mxc/mxcfb_siihdmi.c index 68e6466cebf..1cff5f71102 100644 --- a/drivers/video/mxc/mxcfb_siihdmi.c +++ b/drivers/video/mxc/mxcfb_siihdmi.c @@ -256,25 +256,52 @@ static int siihdmi_read_edid(struct siihdmi_tx *tx, u8 *edid, size_t size) return 0; } +static void siihdmi_parse_cea_extension(struct siihdmi_tx *tx, + struct cea_timing_block *ctb) +{ + tx->enable_audio = ctb->basic_audio_supported; + + if (ctb->underscan_supported) + tx->pixel_mapping = PIXEL_MAPPING_UNDERSCANNED; + + if (ctb->dtd_start_offset == CTB_DTD_INVALID) + return; + #if 0 -static void siihdmi_parse_audio(struct siihdmi_tx *tx, struct cea_dbc_audio *audio) -{ -} + /* okay DTD data is off in the wild reaches so the next blocks + * will be audio, video, vendor and speaker configuration + */ + u8 length, offset = 0x5; + struct cea_dbc_header *dbc_header = (struct cea_dbc_header *) &ctb->dbc_start_offset; -static void siihdmi_parse_video(struct siihdmi_tx *tx, struct cea_dbc_video *video) -{ -} - -static void siihdmi_parse_speaker(struct siihdmi_tx *tx, struct cea_dbc_speaker *speaker) -{ -} - -static void siihdmi_parse_vendor(struct siihdmi_tx *tx, struct cea_dbc_vendor *vendor) -{ -} + while (offset <= 127) { + switch(dbc_header->block_type_tag) { + case CEA_DATA_BLOCK_TAG_AUDIO: + length = dbc_header->length; + siihdmi_parse_audio(tx, (struct cea_dbc_audio *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); + offset += length; + break; + case CEA_DATA_BLOCK_TAG_VIDEO: + length = dbc_header->length; + siihdmi_parse_video(tx, (struct cea_dbc_video *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); + offset += length; + break; + case CEA_DATA_BLOCK_TAG_SPEAKER: + length = dbc_header->length; + siihdmi_parse_speaker(tx, (struct cea_dbc_speaker *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); + offset += length; + break; + case CEA_DATA_BLOCK_TAG_VENDOR: + length = dbc_header->length; + siihdmi_parse_vendor(tx, (struct cea_dbc_vendor *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); + offset += length; + break; + } + } #endif +} -static void siihdmi_detect_sink(struct siihdmi_tx *tx, u8 *edid, size_t size) +static void siihdmi_detect_sink(struct siihdmi_tx *tx) { /* * Sink detection is a fairly simple matter. Assume that we are @@ -284,61 +311,28 @@ static void siihdmi_detect_sink(struct siihdmi_tx *tx, u8 *edid, size_t size) * timing block data reports support for audio, then the sink is HDMI. */ - struct cea_timing_block *ctb; + u8 edid[EDID_BLOCK_SIZE << 1]; + const struct edid_block0 * const block0 = (struct edid_block0 *) edid; + const struct edid_extension * const block1 = + (struct edid_extension *) edid + EDID_BLOCK_SIZE; tx->connection_type = CONNECTION_TYPE_DVI; tx->pixel_mapping = PIXEL_MAPPING_EXACT; tx->enable_audio = false; - if (edid[EEDID_EXTENSION_FLAG]) { - switch (edid[EEDID_EXTENSION_TAG]) { - case EDID_EXTENSION_CEA: - ctb = (struct cea_timing_block *) &edid[EEDID_EXTENSION_DATA_OFFSET]; + if (siihdmi_read_edid(tx, edid, sizeof(edid)) < 0) + return; - if (ctb->basic_audio_supported) { - tx->connection_type = CONNECTION_TYPE_HDMI; // EEEEE. this should be in the vendor block below - tx->enable_audio = true; - } + if (!block0->extensions) + return; - if (ctb->underscan_supported) - tx->pixel_mapping = PIXEL_MAPPING_UNDERSCANNED; -#if 0 - if (ctb->dtd_start_offset != 0x4) { - /* okay DTD data is off in the wild reaches so the next blocks - * will be audio, video, vendor and speaker configuration - */ - u8 length, offset = 0x5; - struct cea_dbc_header *dbc_header = (struct cea_dbc_header *) &ctb->dbc_start_offset; - - while (offset <= 127) { - switch(dbc_header->block_type_tag) { - case CEA_DATA_BLOCK_TAG_AUDIO: - length = dbc_header->length; - siihdmi_parse_audio(tx, (struct cea_dbc_audio *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); - offset += length; - break; - case CEA_DATA_BLOCK_TAG_VIDEO: - length = dbc_header->length; - siihdmi_parse_video(tx, (struct cea_dbc_video *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); - offset += length; - break; - case CEA_DATA_BLOCK_TAG_SPEAKER: - length = dbc_header->length; - siihdmi_parse_speaker(tx, (struct cea_dbc_speaker *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); - offset += length; - break; - case CEA_DATA_BLOCK_TAG_VENDOR: - length = dbc_header->length; - siihdmi_parse_vendor(tx, (struct cea_dbc_vendor *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); - offset += length; - break; - } - } - } -#endif - default: - break; - } + switch (block1->tag) { + case EDID_EXTENSION_CEA: + siihdmi_parse_cea_extension(tx, + (struct cea_timing_block *) block1); + break; + default: + break; } } @@ -683,19 +677,18 @@ static void siihdmi_dump_modelines(const struct fb_monspecs * const monspecs) static int siihdmi_init_fb(struct siihdmi_tx *tx, struct fb_info *fb) { - u8 edid[EEDID_BASE_LENGTH]; + const struct edid_block0 edid; const struct fb_videomode *mode = NULL; struct fb_var_screeninfo var = {0}; int ret; - /* TODO use platform_data to prune modelist */ + siihdmi_detect_sink(tx); - if ((ret = siihdmi_read_edid(tx, edid, sizeof(edid))) < 0) + /* TODO use platform_data to prune modelist */ + if ((ret = siihdmi_read_edid(tx, (u8 *) &edid, sizeof(edid))) < 0) return ret; - siihdmi_detect_sink(tx, edid, sizeof(edid)); - - fb_edid_to_monspecs(edid, &fb->monspecs); + fb_edid_to_monspecs((u8 *) &edid, &fb->monspecs); siihdmi_dump_modelines(&fb->monspecs); /* TODO mxcfb_videomode_to_modelist did some additional work */ fb_videomode_to_modelist(fb->monspecs.modedb, diff --git a/include/linux/edid.h b/include/linux/edid.h index b9db91b72ca..a706d5b3729 100644 --- a/include/linux/edid.h +++ b/include/linux/edid.h @@ -35,18 +35,14 @@ /* EDID constants and Structures */ #define EDID_I2C_DDC_DATA_ADDRESS (0x50) -#define EEDID_BASE_LENGTH (0x100) +#define EDID_BLOCK_SIZE (0x80) +#define EDID_MAX_EXTENSIONS (0xfe) -#define EEDID_EXTENSION_FLAG (0x7e) +#define EDID_MAGIC (0x00ffffffffffff00) -#define EEDID_EXTENSION_TAG (0x80) -#define EEDID_EXTENSION_DATA_OFFSET (0x80) -#define EEDID_CEA_VENDOR_SPECIFIC_TAG (0x65) -#define EEDID_CEA_VENDOR_SPECIFIC_OFFSET (0x94) - -enum edid_extension { - EDID_EXTENSION_TIMING = 0x00, // Timing Extension +enum edid_extension_type { + EDID_EXTENSION_TIMING = 0x01, // Timing Extension EDID_EXTENSION_CEA = 0x02, // Additional Timing Block Data (CEA EDID Timing Extension) EDID_EXTENSION_VTB = 0x10, // Video Timing Block Extension (VTB-EXT) EDID_EXTENSION_EDID_2_0 = 0x20, // EDID 2.0 Extension @@ -60,5 +56,302 @@ enum edid_extension { EDID_EXTENSION_DDDB = 0xff, // Display Device Data Block (DDDB) }; +enum edid_display_type { + EDID_DISPLAY_TYPE_MONOCHROME, + EDID_DISPLAY_TYPE_RGB, + EDID_DISPLAY_TYPE_NON_RGB, + EDID_DISPLAY_TYPE_UNDEFINED, +}; + +enum edid_aspect_ratio { + EDID_ASPECT_RATIO_16_10, + EDID_ASPECT_RATIO_4_3, + EDID_ASPECT_RATIO_5_4, + EDID_ASPECT_RATIO_16_9, +}; + +enum edid_monitor_descriptor_type { + EDID_MONITOR_DESCRIPTOR_STANDARD_TIMING_IDENTIFIERS = 0xfa, + EDID_MONITOR_DESCRIPTOR_COLOR_POINT = 0xfb, + EDID_MONITOR_DESCRIPTOR_MONITOR_NAME = 0xfc, + EDID_MONITOR_DESCRIPTOR_MONITOR_RANGE_LIMITS = 0xfd, + EDID_MONITOR_DESCRIPTOR_MONITOR_SERIAL_NUMBER = 0xff, +}; + + +struct __packed edid_detailed_timing_descriptor { + u16 pixel_clock; /* = value * 10000 */ + + u8 horizontal_active_lo; + u8 horizontal_blanking_lo; + + unsigned horizontal_blanking_hi : 4; + unsigned horizontal_active_hi : 4; + + u8 vertical_active_lo; + u8 vertical_blanking_lo; + + unsigned vertical_blanking_hi : 4; + unsigned vertical_active_hi : 4; + + u8 horizontal_sync_offset_lo; + u8 horizontal_sync_pulse_width_lo; + + unsigned vertical_sync_pulse_width_lo : 4; + unsigned vertical_sync_offset_lo : 4; + + unsigned vertical_sync_pulse_width_hi : 2; + unsigned vertical_sync_offset_hi : 2; + unsigned horizontal_sync_pulse_width_hi : 2; + unsigned horizontal_sync_offset_hi : 2; + + u8 horizontal_image_size_lo; + u8 vertical_image_size_lo; + + unsigned vertical_image_size_hi : 4; + unsigned horizontal_image_size_hi : 4; + + u8 horizontal_border; + u8 vertical_border; + + u8 flags; +}; + + +static inline u16 +edid_timing_pixel_clock(const struct edid_detailed_timing_descriptor * const dtb) +{ + return dtb->pixel_clock * 1000; +} + +static inline u16 +edid_timing_horizontal_blanking(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->horizontal_blanking_hi << 8) | dtb->horizontal_blanking_lo; +} + +static inline u16 +edid_timing_horizontal_active(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->horizontal_active_hi << 8) | dtb->horizontal_active_lo; +} + +static inline u16 +edid_timing_vertical_blanking(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->vertical_blanking_hi << 8) | dtb->vertical_blanking_lo; +} + +static inline u16 +edid_timing_vertical_active(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->vertical_active_hi << 8) | dtb->vertical_active_lo; +} + +static inline u8 +edid_timing_vertical_sync_offset(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->vertical_sync_offset_hi << 4) | dtb->vertical_sync_offset_lo; +} + +static inline u8 +edid_timing_vertical_sync_pulse_width(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->vertical_sync_pulse_width_hi << 4) | dtb->vertical_sync_pulse_width_lo; +} + +static inline u8 +edid_timing_horizontal_sync_offset(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->horizontal_sync_offset_hi << 4) | dtb->horizontal_sync_offset_lo; +} + +static inline u8 +edid_timing_horizontal_sync_pulse_width(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->horizontal_sync_pulse_width_hi << 4) | dtb->horizontal_sync_pulse_width_lo; +} + +static inline u16 +edid_timing_horizontal_image_size(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->horizontal_image_size_hi << 8) | dtb->horizontal_image_size_lo; +} + +static inline u16 +edid_timing_vertical_image_size(const struct edid_detailed_timing_descriptor * const dtb) +{ + return (dtb->vertical_image_size_hi << 8) | dtb->vertical_image_size_lo; +} + + +struct __packed edid_monitor_descriptor { + u16 flag0; + u8 flag1; + u8 data_type_tag; + u8 flag2; + u8 data[13]; +}; + +struct __packed edid_block0 { + /* header information */ + u8 header[8]; + + /* vendor/product identification */ + struct { + unsigned id0 : 5; + unsigned id1 : 5; + unsigned id2 : 5; + unsigned zero : 1; + } manufacturer; + + u8 product[2]; + u8 serial_number[4]; + u8 manufacture_week; + u8 manufacture_year; + + /* EDID version */ + u8 version; + u8 revision; + + /* basic display parameters and features */ + struct { + unsigned dfp_1x_vsync_serration : 1; /* VESA DFP 1.x */ + unsigned green_video_sync : 1; + unsigned composite_sync : 1; + unsigned separate_sync : 1; + unsigned blank_to_black_setup : 1; + unsigned signal_level_standard : 2; + unsigned digital : 1; + } video_input_definition; + + u8 maximum_horizontal_image_size; /* cm */ + u8 maximum_vertical_image_size; /* cm */ + u8 display_transfer_characteristics; /* gamma = (value + 100) / 100 */ + + struct { + unsigned default_gtf : 1; /* generalised timing formula */ + unsigned preferred_timing_mode : 1; + unsigned standard_default_color_space : 1; + unsigned display_type : 2; + unsigned active_off : 1; + unsigned suspend : 1; + unsigned standby : 1; + } feature_support; + + /* color characteristics block */ + unsigned green_y_low : 2; + unsigned green_x_low : 2; + unsigned red_y_low : 2; + unsigned red_x_low : 2; + + unsigned white_y_low : 2; + unsigned white_x_low : 2; + unsigned blue_y_low : 2; + unsigned blue_x_low : 2; + + u8 red_x; + u8 red_y; + u8 green_x; + u8 green_y; + u8 blue_x; + u8 blue_y; + u8 white_x; + u8 white_y; + + /* established timings */ + struct { + unsigned timing_800x600_60 : 1; + unsigned timing_800x600_56 : 1; + unsigned timing_640x480_75 : 1; + unsigned timing_640x480_72 : 1; + unsigned timing_640x480_67 : 1; + unsigned timing_640x480_60 : 1; + unsigned timing_720x400_88 : 1; + unsigned timing_720x400_70 : 1; + + unsigned timing_1280x1024_75 : 1; + unsigned timing_1024x768_75 : 1; + unsigned timing_1024x768_60 : 1; + unsigned timing_1024x768_87 : 1; + unsigned timing_832x624_75 : 1; + unsigned timing_800x600_75 : 1; + unsigned timing_800x600_72 : 1; + } established_timings; + + struct { + unsigned reserved : 7; + unsigned timing_1152x870_75 : 1; + } manufacturer_timings; + + /* standard timing id */ + struct { + u8 horizontal_active_pixels; /* = (value + 31) * 8 */ + + unsigned refresh_rate : 6; /* = value + 60 */ + unsigned image_aspect_ratio : 2; + } standard_timing_identification[8]; + + /* detailed timing */ + union { + struct edid_detailed_timing_descriptor detailed_timing[4]; + struct edid_monitor_descriptor monitor_descriptor[4]; + } detailed_timings; + + u8 extensions; + u8 checksum; +}; + +struct __packed edid_color_characteristics_data { + struct { + u16 x; + u16 y; + } red, green, blue, white; +}; + + +static inline u16 edid_gamma(const struct edid_block0 * const block0) +{ + return (block0->display_transfer_characteristics + 100) / 100; +} + +static inline struct edid_color_characteristics_data +edid_color_characteristics(const struct edid_block0 * const block0) +{ + struct edid_color_characteristics_data characteristics = { + .red = { + .x = (block0->red_x << 8) | block0->red_x_low, + .y = (block0->red_y << 8) | block0->red_y_low, + }, + .green = { + .x = (block0->green_x << 8) | block0->green_x_low, + .y = (block0->green_y << 8) | block0->green_y_low, + }, + .blue = { + .x = (block0->blue_x << 8) | block0->blue_x_low, + .y = (block0->blue_y << 8) | block0->blue_y_low, + }, + .white = { + .x = (block0->white_x << 8) | block0->white_x_low, + .y = (block0->white_y << 8) | block0->white_y_low, + }, + }; + + return characteristics; +} + +struct __packed edid_block_map { + u8 tag; + u8 extension_tag[126]; + u8 checksum; +}; + +struct __packed edid_extension { + u8 tag; + u8 revision; + u8 extension_data[125]; + u8 checksum; +}; + #endif