[U-Boot] [PATCH v2 00/11] video: Add / fix videomodes modes, use for sunxi, enable EDID on sunxi

Hi Anatolij & Ian,
Here is v2 of my patch-series to make the video-mode configurable on sunxi and use EDID where available.
In version 2 I've switched to using the existing infra in videomodes.c as much as possible, including using the standard video-mode env. variable, and the existing functions to parse it.
Anatolij this series starts with a bunch of fixes / additions to videomodes.c, can you please review (and ack :) these ? Since the only user of the new functionality is sunxi code I would like to take these upstream through u-boot-sunxi/next if that is ok with you, but I do need your ack for them before merging them there.
Ian, can you review the sunxi specific patches ?
Thanks & Regards,
Hans

Add pixelclock_khz and refresh fields to ctfb_res_modes:
1) pixelclocks are usually referred to in hz, not picoseconds, and e.g pll-s are also typically programmed in hz, not ps. Converting between the 2 leads to rounding differences, add a pixelclock_khz field to directly store the *exact* pixelclock for a mode, so that drivers do not need to resort to rounding tricks to try and guess the exact pixelclock;
2) The video-mode environment variable, as parsed by video_get_video_mode also contains the vertical refresh rate, add a refresh field, so that the refresh-rate can be matched when parsing the video-mode environment variable.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/videomodes.c | 14 +++++++------- drivers/video/videomodes.h | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 18c1f3d..8f8a919 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -84,13 +84,13 @@ const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = { {0x31B, RES_MODE_1280x1024, 24}, }; const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { - /* x y pixclk le ri up lo hs vs s vmode */ - {640, 480, 39721, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, - {800, 600, 27778, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, - {1024, 768, 15384, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, - {960, 720, 13100, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, - {1152, 864, 12004, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, - {1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, + /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */ + { 640, 480, 60, 39721, 25180, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, + { 800, 600, 60, 27778, 36000, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, + {1024, 768, 60, 15384, 65000, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, + { 960, 720, 80, 13100, 76335, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, + {1152, 864, 60, 12004, 83300, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, + {1280, 1024, 60, 9090, 110000, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, };
/************************************************************************ diff --git a/drivers/video/videomodes.h b/drivers/video/videomodes.h index d83993a..94b13e6 100644 --- a/drivers/video/videomodes.h +++ b/drivers/video/videomodes.h @@ -35,8 +35,10 @@ struct ctfb_res_modes { int xres; /* visible resolution */ int yres; + int refresh; /* vertical refresh rate in hz */ /* Timing: All values in pixclocks, except pixclock (of course) */ int pixclock; /* pixel clock in ps (pico seconds) */ + int pixclock_khz; /* pixel clock in kHz */ int left_margin; /* time from sync to picture */ int right_margin; /* time from picture to sync */ int upper_margin; /* time from sync to picture */

On Fri, 19 Dec 2014 18:10:31 +0100 Hans de Goede hdegoede@redhat.com wrote:
Add pixelclock_khz and refresh fields to ctfb_res_modes:
- pixelclocks are usually referred to in hz, not picoseconds, and e.g
pll-s are also typically programmed in hz, not ps. Converting between the 2 leads to rounding differences, add a pixelclock_khz field to directly store the *exact* pixelclock for a mode, so that drivers do not need to resort to rounding tricks to try and guess the exact pixelclock;
- The video-mode environment variable, as parsed by video_get_video_mode
also contains the vertical refresh rate, add a refresh field, so that the refresh-rate can be matched when parsing the video-mode environment variable.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

The timings for the modes defined in videomodes.c differ (significantly) from vesa standard timings for these modes.
This commit adds a version with the proper std timings for these modes, since I do not want to cause regressions, boards which want to use the standard timings need to define CONFIG_VIDEO_STD_TIMINGS to get the new correct timings.
Since there is no std timing for 960x720 this commit uses the timing used by the nvidia video drivers for 960x720, which uses a standard pixelclock of 74.25 MHz rather then the weird 76.335... clock used by the old modes.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/videomodes.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 8f8a919..a363482 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -85,12 +85,21 @@ const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = { }; const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */ +#ifndef CONFIG_VIDEO_STD_TIMINGS { 640, 480, 60, 39721, 25180, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, { 800, 600, 60, 27778, 36000, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, {1024, 768, 60, 15384, 65000, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, { 960, 720, 80, 13100, 76335, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, {1152, 864, 60, 12004, 83300, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, {1280, 1024, 60, 9090, 110000, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, +#else + { 640, 480, 60, 39683, 25200, 48, 16, 33, 10, 96, 2, 0, FB_VMODE_NONINTERLACED}, + { 800, 600, 60, 25000, 40000, 88, 40, 23, 1, 128, 4, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, + {1024, 768, 60, 15384, 65000, 160, 24, 29, 3, 136, 6, 0, FB_VMODE_NONINTERLACED}, + { 960, 720, 75, 13468, 74250, 176, 72, 27, 1, 112, 2, 0, FB_VMODE_NONINTERLACED}, + {1152, 864, 75, 9259, 108000, 256, 64, 32, 1, 128, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, + {1280, 1024, 60, 9259, 108000, 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, +#endif };
/************************************************************************

On Fri, 19 Dec 2014 18:10:32 +0100 Hans de Goede hdegoede@redhat.com wrote:
The timings for the modes defined in videomodes.c differ (significantly) from vesa standard timings for these modes.
This commit adds a version with the proper std timings for these modes, since I do not want to cause regressions, boards which want to use the standard timings need to define CONFIG_VIDEO_STD_TIMINGS to get the new correct timings.
Since there is no std timing for 960x720 this commit uses the timing used by the nvidia video drivers for 960x720, which uses a standard pixelclock of 74.25 MHz rather then the weird 76.335... clock used by the old modes.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

Add modes useful for hd-tvs and modern monitors.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/videomodes.c | 4 ++++ drivers/video/videomodes.h | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index a363482..595ea3f 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -100,6 +100,10 @@ const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { {1152, 864, 75, 9259, 108000, 256, 64, 32, 1, 128, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, {1280, 1024, 60, 9259, 108000, 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, #endif + {1280, 720, 60, 13468, 74250, 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, + {1360, 768, 60, 11696, 85500, 256, 64, 17, 3, 112, 7, 0, FB_VMODE_NONINTERLACED}, + {1920, 1080, 60, 6734, 148500, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, + {1920, 1200, 60, 6494, 154000, 80, 48, 26, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED}, };
/************************************************************************ diff --git a/drivers/video/videomodes.h b/drivers/video/videomodes.h index 94b13e6..579c685 100644 --- a/drivers/video/videomodes.h +++ b/drivers/video/videomodes.h @@ -64,7 +64,11 @@ struct ctfb_vesa_modes { #define RES_MODE_960_720 3 #define RES_MODE_1152x864 4 #define RES_MODE_1280x1024 5 -#define RES_MODES_COUNT 6 +#define RES_MODE_1280x720 6 +#define RES_MODE_1360x768 7 +#define RES_MODE_1920x1080 8 +#define RES_MODE_1920x1200 9 +#define RES_MODES_COUNT 10
#define VESA_MODES_COUNT 19

On Fri, 19 Dec 2014 18:10:33 +0100 Hans de Goede hdegoede@redhat.com wrote:
Add modes useful for hd-tvs and modern monitors.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

Add a video_get_ctfb_res_modes() helper function, which uses video_get_video_mode() to parse the 'video-mode' environment variable and then looks up the matching mode in res_mode_init and returns the matching mode.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/videomodes.c | 40 ++++++++++++++++++++++++++++++++++++++++ drivers/video/videomodes.h | 5 +++++ 2 files changed, 45 insertions(+)
diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 595ea3f..72841fd 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -273,3 +273,43 @@ int video_get_video_mode(unsigned int *xres, unsigned int *yres,
return 1; } + +/* + * Parse the 'video-mode' environment variable using video_get_video_mode() + * and lookup the matching ctfb_res_modes in res_mode_init. + * + * @default_mode: RES_MODE_##x## define for the mode to store in mode_ret + * when 'video-mode' is not set or does not contain a valid mode + * @default_depth: depth to set when 'video-mode' is not set + * @mode_ret: pointer where the mode will be stored + * @depth_ret: pointer where the depth will be stored + * @options: pointer to any remaining options, or NULL + */ +void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, + const struct ctfb_res_modes **mode_ret, + unsigned int *depth_ret, + const char **options) +{ + unsigned int i, xres, yres, depth, refresh; + + *mode_ret = &res_mode_init[default_mode]; + *depth_ret = default_depth; + *options = NULL; + + if (!video_get_video_mode(&xres, &yres, &depth, &refresh, options)) + return; + + for (i = 0; i < RES_MODES_COUNT; i++) { + if (res_mode_init[i].xres == xres && + res_mode_init[i].yres == yres && + res_mode_init[i].refresh == refresh) { + *mode_ret = &res_mode_init[i]; + *depth_ret = depth; + return; + } + } + + printf("video-mode %dx%d-%d@%d not available, falling back to %dx%d-%d@%d\n", + xres, yres, depth, refresh, (*mode_ret)->xres, + (*mode_ret)->yres, *depth_ret, (*mode_ret)->refresh); +} diff --git a/drivers/video/videomodes.h b/drivers/video/videomodes.h index 579c685..02419cd 100644 --- a/drivers/video/videomodes.h +++ b/drivers/video/videomodes.h @@ -79,3 +79,8 @@ int video_get_params (struct ctfb_res_modes *pPar, char *penv);
int video_get_video_mode(unsigned int *xres, unsigned int *yres, unsigned int *depth, unsigned int *freq, const char **options); + +void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, + const struct ctfb_res_modes **mode_ret, + unsigned int *depth_ret, + const char **options);

On Fri, 19 Dec 2014 18:10:34 +0100 Hans de Goede hdegoede@redhat.com wrote:
Add a video_get_ctfb_res_modes() helper function, which uses video_get_video_mode() to parse the 'video-mode' environment variable and then looks up the matching mode in res_mode_init and returns the matching mode.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

Add 2 helper functions to get strings, reps. ints from the options value returned by video_get_video_mode() / video_get_ctfb_res_modes().
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/videomodes.c | 60 +++++++++++++++++++++++++++++++++++++++++++++- drivers/video/videomodes.h | 5 ++++ 2 files changed, 64 insertions(+), 1 deletion(-)
diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 72841fd..1f79d5f 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -113,7 +113,7 @@ const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { * returns the length to the next seperator */ static int -video_get_param_len (char *start, char sep) +video_get_param_len (const char *start, char sep) { int i = 0; while ((*start != 0) && (*start != sep)) { @@ -313,3 +313,61 @@ void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, xres, yres, depth, refresh, (*mode_ret)->xres, (*mode_ret)->yres, *depth_ret, (*mode_ret)->refresh); } + +/* + * Find the named string option within the ',' seperated options string, and + * store its value in dest. + * + * @options: ',' seperated options string + * @name: name of the option to look for + * @dest: destination buffer to store the value of the option in + * @dest_len: length of dest + * @def: value to store in dest if the option is not present in options + */ +void video_get_option_string(const char *options, const char *name, + char *dest, int dest_len, const char *def) +{ + const char *p = options; + const int name_len = strlen(name); + int i, len; + + while (p && (i = video_get_param_len (p, ',')) != 0) { + if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') { + len = i - (name_len + 1); + if (len >= dest_len) + len = dest_len - 1; + memcpy(dest, &p[name_len + 1], len); + dest[len] = 0; + return; + } + p += i; + if (*p != 0) + p++; /* skip ',' */ + } + strcpy(dest, def); +} + +/* + * Find the named integer option within the ',' seperated options string, and + * return its value. + * + * @options: ',' seperated options string + * @name: name of the option to look for + * @def: value to return if the option is not present in options + */ +int video_get_option_int(const char *options, const char *name, int def) +{ + const char *p = options; + const int name_len = strlen(name); + int i; + + while (p && (i = video_get_param_len (p, ',')) != 0) { + if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') + return simple_strtoul(&p[name_len + 1], NULL, 10); + + p += i; + if (*p != 0) + p++; /* skip ',' */ + } + return def; +} diff --git a/drivers/video/videomodes.h b/drivers/video/videomodes.h index 02419cd..047b8a9 100644 --- a/drivers/video/videomodes.h +++ b/drivers/video/videomodes.h @@ -84,3 +84,8 @@ void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, const struct ctfb_res_modes **mode_ret, unsigned int *depth_ret, const char **options); + +void video_get_option_string(const char *options, const char *name, + char *dest, int dest_len, const char *def); + +int video_get_option_int(const char *options, const char *name, int def);

On Fri, 2014-12-19 at 18:10 +0100, Hans de Goede wrote:
Add 2 helper functions to get strings, reps. ints from the options value returned by video_get_video_mode() / video_get_ctfb_res_modes().
I can't quite parse this, what is "reps. ints"?
Also, it's "separated" not "seperated" (throughout).
Signed-off-by: Hans de Goede hdegoede@redhat.com
drivers/video/videomodes.c | 60 +++++++++++++++++++++++++++++++++++++++++++++- drivers/video/videomodes.h | 5 ++++ 2 files changed, 64 insertions(+), 1 deletion(-)
diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 72841fd..1f79d5f 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -113,7 +113,7 @@ const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = {
- returns the length to the next seperator
*/ static int -video_get_param_len (char *start, char sep) +video_get_param_len (const char *start, char sep) { int i = 0; while ((*start != 0) && (*start != sep)) { @@ -313,3 +313,61 @@ void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, xres, yres, depth, refresh, (*mode_ret)->xres, (*mode_ret)->yres, *depth_ret, (*mode_ret)->refresh); }
+/*
- Find the named string option within the ',' seperated options string, and
- store its value in dest.
- @options: ',' seperated options string
- @name: name of the option to look for
- @dest: destination buffer to store the value of the option in
- @dest_len: length of dest
- @def: value to store in dest if the option is not present in options
- */
+void video_get_option_string(const char *options, const char *name,
char *dest, int dest_len, const char *def)
+{
- const char *p = options;
- const int name_len = strlen(name);
- int i, len;
- while (p && (i = video_get_param_len (p, ',')) != 0) {
if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') {
len = i - (name_len + 1);
if (len >= dest_len)
len = dest_len - 1;
memcpy(dest, &p[name_len + 1], len);
dest[len] = 0;
return;
}
p += i;
if (*p != 0)
p++; /* skip ',' */
- }
- strcpy(dest, def);
+}
+/*
- Find the named integer option within the ',' seperated options string, and
- return its value.
- @options: ',' seperated options string
- @name: name of the option to look for
- @def: value to return if the option is not present in options
- */
+int video_get_option_int(const char *options, const char *name, int def) +{
- const char *p = options;
- const int name_len = strlen(name);
- int i;
- while (p && (i = video_get_param_len (p, ',')) != 0) {
if (strncmp(p, name, name_len) == 0 && p[name_len] == '=')
return simple_strtoul(&p[name_len + 1], NULL, 10);
p += i;
if (*p != 0)
p++; /* skip ',' */
- }
- return def;
+} diff --git a/drivers/video/videomodes.h b/drivers/video/videomodes.h index 02419cd..047b8a9 100644 --- a/drivers/video/videomodes.h +++ b/drivers/video/videomodes.h @@ -84,3 +84,8 @@ void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, const struct ctfb_res_modes **mode_ret, unsigned int *depth_ret, const char **options);
+void video_get_option_string(const char *options, const char *name,
char *dest, int dest_len, const char *def);
+int video_get_option_int(const char *options, const char *name, int def);

Hi,
On 22-12-14 13:35, Ian Campbell wrote:
On Fri, 2014-12-19 at 18:10 +0100, Hans de Goede wrote:
Add 2 helper functions to get strings, reps. ints from the options value returned by video_get_video_mode() / video_get_ctfb_res_modes().
I can't quite parse this, what is "reps. ints"?
Oops sorry, that should be resp. for respectively, so with the abbreviations dropped it should read:
"Add 2 helper functions to get strings, respectively integers from the options value returned by video_get_video_mode() / video_get_ctfb_res_modes()."
Which hopefully is more clear, I've fixed this in my personal tree.
Also, it's "separated" not "seperated" (throughout).
I've also fixed this in my personal tree.
Thanks & Regards,
Hans
Signed-off-by: Hans de Goede hdegoede@redhat.com
drivers/video/videomodes.c | 60 +++++++++++++++++++++++++++++++++++++++++++++- drivers/video/videomodes.h | 5 ++++ 2 files changed, 64 insertions(+), 1 deletion(-)
diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 72841fd..1f79d5f 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -113,7 +113,7 @@ const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = {
- returns the length to the next seperator
*/ static int -video_get_param_len (char *start, char sep) +video_get_param_len (const char *start, char sep) { int i = 0; while ((*start != 0) && (*start != sep)) { @@ -313,3 +313,61 @@ void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, xres, yres, depth, refresh, (*mode_ret)->xres, (*mode_ret)->yres, *depth_ret, (*mode_ret)->refresh); }
+/*
- Find the named string option within the ',' seperated options string, and
- store its value in dest.
- @options: ',' seperated options string
- @name: name of the option to look for
- @dest: destination buffer to store the value of the option in
- @dest_len: length of dest
- @def: value to store in dest if the option is not present in options
- */
+void video_get_option_string(const char *options, const char *name,
char *dest, int dest_len, const char *def)
+{
- const char *p = options;
- const int name_len = strlen(name);
- int i, len;
- while (p && (i = video_get_param_len (p, ',')) != 0) {
if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') {
len = i - (name_len + 1);
if (len >= dest_len)
len = dest_len - 1;
memcpy(dest, &p[name_len + 1], len);
dest[len] = 0;
return;
}
p += i;
if (*p != 0)
p++; /* skip ',' */
- }
- strcpy(dest, def);
+}
+/*
- Find the named integer option within the ',' seperated options string, and
- return its value.
- @options: ',' seperated options string
- @name: name of the option to look for
- @def: value to return if the option is not present in options
- */
+int video_get_option_int(const char *options, const char *name, int def) +{
- const char *p = options;
- const int name_len = strlen(name);
- int i;
- while (p && (i = video_get_param_len (p, ',')) != 0) {
if (strncmp(p, name, name_len) == 0 && p[name_len] == '=')
return simple_strtoul(&p[name_len + 1], NULL, 10);
p += i;
if (*p != 0)
p++; /* skip ',' */
- }
- return def;
+} diff --git a/drivers/video/videomodes.h b/drivers/video/videomodes.h index 02419cd..047b8a9 100644 --- a/drivers/video/videomodes.h +++ b/drivers/video/videomodes.h @@ -84,3 +84,8 @@ void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, const struct ctfb_res_modes **mode_ret, unsigned int *depth_ret, const char **options);
+void video_get_option_string(const char *options, const char *name,
char *dest, int dest_len, const char *def);
+int video_get_option_int(const char *options, const char *name, int def);

On Fri, 19 Dec 2014 18:10:35 +0100 Hans de Goede hdegoede@redhat.com wrote:
Add 2 helper functions to get strings, reps. ints from the options value returned by video_get_video_mode() / video_get_ctfb_res_modes().
Signed-off-by: Hans de Goede hdegoede@redhat.com
before applying please fix these checkpatch warnings:
WARNING: space prohibited between function name and open parenthesis '(' #32: FILE: drivers/video/videomodes.c:116: +video_get_param_len (const char *start, char sep)
WARNING: space prohibited between function name and open parenthesis '(' #58: FILE: drivers/video/videomodes.c:334: + while (p && (i = video_get_param_len (p, ',')) != 0) {
WARNING: space prohibited between function name and open parenthesis '(' #88: FILE: drivers/video/videomodes.c:364: + while (p && (i = video_get_param_len (p, ',')) != 0) {
total: 0 errors, 3 warnings, 0 checks, 77 lines checked
With these warnings fixed,
Acked-by: Anatolij Gustschin agust@denx.de

Add a video_edid_dtd_to_ctfb_res_modes helper function to convert an EDID detailed timing to a struct ctfb_res_modes.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/videomodes.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/video/videomodes.h | 4 +++ include/edid.h | 4 +++ 3 files changed, 81 insertions(+)
diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 1f79d5f..9a4f0af 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -58,6 +58,8 @@ ****************************************************************************/
#include <common.h> +#include <edid.h> +#include <errno.h> #include <linux/ctype.h>
#include "videomodes.h" @@ -371,3 +373,74 @@ int video_get_option_int(const char *options, const char *name, int def) } return def; } + +/** + * Convert an EDID detailed timing to a struct ctfb_res_modes + * + * @param t The EDID detailed timing to be converted + * @param mode Returns the converted timing + * + * @return 0 on success, or a negative errno on error + */ +int video_edid_dtd_to_ctfb_res_modes(struct edid_detailed_timing *t, + struct ctfb_res_modes *mode) +{ + int margin, h_total, v_total; + + /* Check all timings are non 0 */ + if (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) == 0 || + EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t) == 0 || + EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) == 0 || + EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t) == 0 || + EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) == 0 || + EDID_DETAILED_TIMING_HSYNC_OFFSET(*t) == 0 || + EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t) == 0 || + EDID_DETAILED_TIMING_VSYNC_OFFSET(*t) == 0 || + EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t) == 0 || + /* 3d formats are not supported*/ + EDID_DETAILED_TIMING_FLAG_STEREO(*t) != 0) + return -EINVAL; + + mode->xres = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t); + mode->yres = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t); + + h_total = mode->xres + EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t); + v_total = mode->yres + EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t); + mode->refresh = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) / + (h_total * v_total); + + mode->pixclock = 1000000000000LL / EDID_DETAILED_TIMING_PIXEL_CLOCK(*t); + mode->pixclock_khz = (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) + 500) / + 1000; + + mode->right_margin = EDID_DETAILED_TIMING_HSYNC_OFFSET(*t); + mode->hsync_len = EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t); + margin = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) - + (mode->right_margin + mode->hsync_len); + if (margin <= 0) + return -EINVAL; + + mode->left_margin = margin; + + mode->lower_margin = EDID_DETAILED_TIMING_VSYNC_OFFSET(*t); + mode->vsync_len = EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t); + margin = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) - + (mode->lower_margin + mode->vsync_len); + if (margin <= 0) + return -EINVAL; + + mode->upper_margin = margin; + + mode->sync = 0; + if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY(*t)) + mode->sync |= FB_SYNC_HOR_HIGH_ACT; + if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY(*t)) + mode->sync |= FB_SYNC_VERT_HIGH_ACT; + + if (EDID_DETAILED_TIMING_FLAG_INTERLACED(*t)) + mode->vmode = FB_VMODE_INTERLACED; + else + mode->vmode = FB_VMODE_NONINTERLACED; + + return 0; +} diff --git a/drivers/video/videomodes.h b/drivers/video/videomodes.h index 047b8a9..82190a2 100644 --- a/drivers/video/videomodes.h +++ b/drivers/video/videomodes.h @@ -5,6 +5,7 @@ * SPDX-License-Identifier: GPL-2.0+ */
+#include <edid.h>
#ifndef CONFIG_SYS_DEFAULT_VIDEO_MODE #define CONFIG_SYS_DEFAULT_VIDEO_MODE 0x301 @@ -89,3 +90,6 @@ void video_get_option_string(const char *options, const char *name, char *dest, int dest_len, const char *def);
int video_get_option_int(const char *options, const char *name, int def); + +int video_edid_dtd_to_ctfb_res_modes(struct edid_detailed_timing *t, + struct ctfb_res_modes *mode); diff --git a/include/edid.h b/include/edid.h index 480a773..d3cc523 100644 --- a/include/edid.h +++ b/include/edid.h @@ -86,6 +86,10 @@ struct edid_detailed_timing { GET_BITS((_x).flags, 4, 3) #define EDID_DETAILED_TIMING_FLAG_POLARITY(_x) \ GET_BITS((_x).flags, 2, 1) +#define EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY(_x) \ + GET_BIT((_x).flags, 2) +#define EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY(_x) \ + GET_BIT((_x).flags, 1) #define EDID_DETAILED_TIMING_FLAG_INTERLEAVED(_x) \ GET_BIT((_x).flags, 0) } __attribute__ ((__packed__));

On Fri, 19 Dec 2014 18:10:36 +0100 Hans de Goede hdegoede@redhat.com wrote:
Add a video_edid_dtd_to_ctfb_res_modes helper function to convert an EDID detailed timing to a struct ctfb_res_modes.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

Add a helper function to check the checksum of an EDID data block.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- common/edid.c | 12 ++++++++++++ include/edid.h | 9 +++++++++ 2 files changed, 21 insertions(+)
diff --git a/common/edid.c b/common/edid.c index e66108f..df797fc 100644 --- a/common/edid.c +++ b/common/edid.c @@ -12,6 +12,7 @@
#include <common.h> #include <edid.h> +#include <errno.h> #include <linux/ctype.h> #include <linux/string.h>
@@ -29,6 +30,17 @@ int edid_check_info(struct edid1_info *edid_info) return 0; }
+int edid_check_checksum(u8 *edid_block) +{ + u8 checksum = 0; + int i; + + for (i = 0; i < 128; i++) + checksum += edid_block[i]; + + return (checksum == 0) ? 0 : -EINVAL; +} + int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin, unsigned int *hmax, unsigned int *vmin, unsigned int *vmax) diff --git a/include/edid.h b/include/edid.h index d3cc523..a69f43a 100644 --- a/include/edid.h +++ b/include/edid.h @@ -246,6 +246,15 @@ void edid_print_info(struct edid1_info *edid_info); int edid_check_info(struct edid1_info *info);
/** + * Check checksum of a 128 bytes EDID data block + * + * @param edid_block EDID block data + * + * @return 0 on success, or a negative errno on error + */ +int edid_check_checksum(u8 *edid_block); + +/** * Get the horizontal and vertical rate ranges of the monitor. * * @param edid The EDID info

On Fri, 19 Dec 2014 18:10:37 +0100 Hans de Goede hdegoede@redhat.com wrote:
Add a helper function to check the checksum of an EDID data block.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

Switch from fb_videomode to ctfb_res_modes and use the predefined videotimings from videomodes.c, rather then defining our own.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/Makefile | 2 +- drivers/video/sunxi_display.c | 41 +++++++++++------------------------------ include/configs/sunxi-common.h | 1 + 3 files changed, 13 insertions(+), 31 deletions(-)
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 00b563f..42b1eaa 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -39,7 +39,7 @@ obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o obj-$(CONFIG_VIDEO_SED13806) += sed13806.o obj-$(CONFIG_VIDEO_SM501) += sm501.o obj-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o -obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o +obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o videomodes.o obj-$(CONFIG_VIDEO_TEGRA) += tegra.o obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o obj-$(CONFIG_VIDEO_X86) += x86_fb.o diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index d241397..cedb56e 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -15,8 +15,8 @@ #include <asm/io.h> #include <fdtdec.h> #include <fdt_support.h> -#include <linux/fb.h> #include <video_fb.h> +#include "videomodes.h"
DECLARE_GLOBAL_DATA_PTR;
@@ -97,7 +97,7 @@ static void sunxi_composer_init(void) setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE); }
-static void sunxi_composer_mode_set(struct fb_videomode *mode, +static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, unsigned int address) { struct sunxi_de_be_reg * const de_be = @@ -205,7 +205,7 @@ static void sunxi_lcdc_init(void) writel(0xffffffff, &lcdc->tcon1_io_tristate); }
-static void sunxi_lcdc_mode_set(struct fb_videomode *mode, +static void sunxi_lcdc_mode_set(const struct ctfb_res_modes *mode, int *clk_div, int *clk_double) { struct sunxi_lcdc_reg * const lcdc = @@ -240,7 +240,7 @@ static void sunxi_lcdc_mode_set(struct fb_videomode *mode, writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len), &lcdc->tcon1_timing_sync);
- sunxi_lcdc_pll_set(mode->pixclock, clk_div, clk_double); + sunxi_lcdc_pll_set(mode->pixclock_khz, clk_div, clk_double); }
#ifdef CONFIG_MACH_SUN6I @@ -255,7 +255,7 @@ static void sunxi_drc_init(void) } #endif
-static void sunxi_hdmi_mode_set(struct fb_videomode *mode, +static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode, int clk_div, int clk_double) { struct sunxi_hdmi_reg * const hdmi = @@ -310,7 +310,7 @@ static void sunxi_engines_init(void) #endif }
-static void sunxi_mode_set(struct fb_videomode *mode, unsigned int address) +static void sunxi_mode_set(const struct ctfb_res_modes *mode, unsigned int address) { struct sunxi_de_be_reg * const de_be = (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; @@ -358,26 +358,7 @@ retry: void *video_hw_init(void) { static GraphicDevice *graphic_device = &sunxi_display.graphic_device; - /* - * Vesa standard 1024x768@60 - * 65.0 1024 1048 1184 1344 768 771 777 806 -hsync -vsync - */ - struct fb_videomode mode = { - .name = "1024x768", - .refresh = 60, - .xres = 1024, - .yres = 768, - .pixclock = 65000, - .left_margin = 160, - .right_margin = 24, - .upper_margin = 29, - .lower_margin = 3, - .hsync_len = 136, - .vsync_len = 6, - .sync = 0, - .vmode = 0, - .flag = 0, - }; + const struct ctfb_res_modes *mode = &res_mode_init[RES_MODE_1024x768]; int ret;
memset(&sunxi_display, 0, sizeof(struct sunxi_display)); @@ -393,9 +374,9 @@ void *video_hw_init(void) printf("HDMI connected.\n"); sunxi_display.enabled = true;
- printf("Setting up a %s console.\n", mode.name); + printf("Setting up a %dx%d console.\n", mode->xres, mode->yres); sunxi_engines_init(); - sunxi_mode_set(&mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE); + sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE);
/* * These are the only members of this structure that are used. All the @@ -405,8 +386,8 @@ void *video_hw_init(void) graphic_device->frameAdrs = gd->fb_base; graphic_device->gdfIndex = GDF_32BIT_X888RGB; graphic_device->gdfBytesPP = 4; - graphic_device->winSizeX = mode.xres; - graphic_device->winSizeY = mode.yres; + graphic_device->winSizeX = mode->xres; + graphic_device->winSizeY = mode->yres;
return graphic_device; } diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index 7b958f8..727f446 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -212,6 +212,7 @@ #define CONFIG_CFB_CONSOLE #define CONFIG_VIDEO_SW_CURSOR #define CONFIG_VIDEO_LOGO +#define CONFIG_VIDEO_STD_TIMINGS
/* allow both serial and cfb console. */ #define CONFIG_CONSOLE_MUX

On Fri, 2014-12-19 at 18:10 +0100, Hans de Goede wrote:
Switch from fb_videomode to ctfb_res_modes and use the predefined videotimings from videomodes.c, rather then defining our own.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

On Fri, 19 Dec 2014 18:10:38 +0100 Hans de Goede hdegoede@redhat.com wrote:
Switch from fb_videomode to ctfb_res_modes and use the predefined videotimings from videomodes.c, rather then defining our own.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

Add support for the standard video-mode environment variable using the videomodes.c video_get_ctfb_res_modes() helper function.
This will allow users to specify the resolution e.g. :
setenv video-mode sunxi:video-mode=1280x1024-24@60 saveenv
Also make the reserved fb mem slightly larger to allow 1920x1200 to work.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/sunxi_display.c | 16 +++++++++++++--- include/configs/sunxi-common.h | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index cedb56e..cf81e45 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -358,7 +358,9 @@ retry: void *video_hw_init(void) { static GraphicDevice *graphic_device = &sunxi_display.graphic_device; - const struct ctfb_res_modes *mode = &res_mode_init[RES_MODE_1024x768]; + const struct ctfb_res_modes *mode; + const char *options; + unsigned int depth; int ret;
memset(&sunxi_display, 0, sizeof(struct sunxi_display)); @@ -367,14 +369,22 @@ void *video_hw_init(void) CONFIG_SUNXI_FB_SIZE >> 10); gd->fb_base = gd->ram_top;
+ video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode, &depth, &options); + ret = sunxi_hdmi_hpd_detect(); if (!ret) return NULL;
printf("HDMI connected.\n"); - sunxi_display.enabled = true;
- printf("Setting up a %dx%d console.\n", mode->xres, mode->yres); + if (mode->vmode != FB_VMODE_NONINTERLACED) { + printf("Only non-interlaced modes supported, falling back to 1024x768\n"); + mode = &res_mode_init[RES_MODE_1024x768]; + } else { + printf("Setting up a %dx%d console\n", mode->xres, mode->yres); + } + + sunxi_display.enabled = true; sunxi_engines_init(); sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE);
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index 727f446..77965f7 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -202,7 +202,7 @@ * The amount of RAM that is reserved for the FB. This will not show up as * RAM to the kernel, but will be reclaimed by a KMS driver in future. */ -#define CONFIG_SUNXI_FB_SIZE (8 << 20) +#define CONFIG_SUNXI_FB_SIZE (9 << 20)
/* Do we want to initialize a simple FB? */ #define CONFIG_VIDEO_DT_SIMPLEFB

On Fri, 2014-12-19 at 18:10 +0100, Hans de Goede wrote:
Add support for the standard video-mode environment variable using the videomodes.c video_get_ctfb_res_modes() helper function.
This will allow users to specify the resolution e.g. :
setenv video-mode sunxi:video-mode=1280x1024-24@60 saveenv
Also make the reserved fb mem slightly larger to allow 1920x1200 to work.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

On Fri, 19 Dec 2014 18:10:39 +0100 Hans de Goede hdegoede@redhat.com wrote:
Add support for the standard video-mode environment variable using the videomodes.c video_get_ctfb_res_modes() helper function.
This will allow users to specify the resolution e.g. :
setenv video-mode sunxi:video-mode=1280x1024-24@60 saveenv
Also make the reserved fb mem slightly larger to allow 1920x1200 to work.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

Allow the user to specify hpd=0 as option in the video-mode env. variable, if hpd is set to 0 then the hdmi output will be brought up even if no cable is connected.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/video/sunxi_display.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index cf81e45..18fa83d 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -53,10 +53,16 @@ static int sunxi_hdmi_hpd_detect(void)
udelay(1000);
- if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT) - return 1; + return (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT) ? 1 : 0; +} + +static void sunxi_hdmi_shutdown(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
- /* No need to keep these running */ clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE); clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE); clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI); @@ -64,8 +70,6 @@ static int sunxi_hdmi_hpd_detect(void) clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI); #endif clock_set_pll3(0); - - return 0; }
/* @@ -361,7 +365,7 @@ void *video_hw_init(void) const struct ctfb_res_modes *mode; const char *options; unsigned int depth; - int ret; + int ret, hpd;
memset(&sunxi_display, 0, sizeof(struct sunxi_display));
@@ -370,12 +374,16 @@ void *video_hw_init(void) gd->fb_base = gd->ram_top;
video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode, &depth, &options); + hpd = video_get_option_int(options, "hpd", 1);
+ /* Always call hdp_detect, as it also enables various clocks, etc. */ ret = sunxi_hdmi_hpd_detect(); - if (!ret) + if (hpd && !ret) { + sunxi_hdmi_shutdown(); return NULL; - - printf("HDMI connected.\n"); + } + if (ret) + printf("HDMI connected: ");
if (mode->vmode != FB_VMODE_NONINTERLACED) { printf("Only non-interlaced modes supported, falling back to 1024x768\n");

On Fri, 2014-12-19 at 18:10 +0100, Hans de Goede wrote:
Allow the user to specify hpd=0 as option in the video-mode env. variable, if hpd is set to 0 then the hdmi output will be brought up even if no cable is connected.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

On Fri, 19 Dec 2014 18:10:40 +0100 Hans de Goede hdegoede@redhat.com wrote:
Allow the user to specify hpd=0 as option in the video-mode env. variable, if hpd is set to 0 then the hdmi output will be brought up even if no cable is connected.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de

Add DDC & EDID support and use it to automatically select the native mode of the attached monitor. This can be disabled by adding edid=0 as option to the video-mode env. variable.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/include/asm/arch-sunxi/display.h | 85 +++++++++++++++++ drivers/video/sunxi_display.c | 154 +++++++++++++++++++++++++++++- include/configs/sunxi-common.h | 1 + 3 files changed, 239 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h index ddb71c1..8c4835e 100644 --- a/arch/arm/include/asm/arch-sunxi/display.h +++ b/arch/arm/include/asm/arch-sunxi/display.h @@ -107,6 +107,48 @@ struct sunxi_hdmi_reg { u32 pad_ctrl1; /* 0x204 */ u32 pll_ctrl; /* 0x208 */ u32 pll_dbg0; /* 0x20c */ + u32 pll_dbg1; /* 0x210 */ + u32 hpd_cec; /* 0x214 */ + u8 res1[0x28]; /* 0x218 */ + u32 spd_pkt; /* 0x240 */ + u8 res2[0xac]; /* 0x244 */ + u32 pkt_ctrl0; /* 0x2f0 */ + u32 pkt_ctrl1; /* 0x2f4 */ + u8 res3[0x18]; /* 0x2f8 */ + u32 audio_sample_count; /* 0x310 */ + u8 res4[0xec]; /* 0x314 */ + u32 audio_tx_fifo; /* 0x400 */ + u8 res5[0xfc]; /* 0x404 */ +#ifndef CONFIG_MACH_SUN6I + u32 ddc_ctrl; /* 0x500 */ + u32 ddc_addr; /* 0x504 */ + u32 ddc_int_mask; /* 0x508 */ + u32 ddc_int_status; /* 0x50c */ + u32 ddc_fifo_ctrl; /* 0x510 */ + u32 ddc_fifo_status; /* 0x514 */ + u32 ddc_fifo_data; /* 0x518 */ + u32 ddc_byte_count; /* 0x51c */ + u32 ddc_cmnd; /* 0x520 */ + u32 ddc_exreg; /* 0x524 */ + u32 ddc_clock; /* 0x528 */ + u8 res6[0x14]; /* 0x52c */ + u32 ddc_line_ctrl; /* 0x540 */ +#else + u32 ddc_ctrl; /* 0x500 */ + u32 ddc_exreg; /* 0x504 */ + u32 ddc_cmnd; /* 0x508 */ + u32 ddc_addr; /* 0x50c */ + u32 ddc_int_mask; /* 0x510 */ + u32 ddc_int_status; /* 0x514 */ + u32 ddc_fifo_ctrl; /* 0x518 */ + u32 ddc_fifo_status; /* 0x51c */ + u32 ddc_clock; /* 0x520 */ + u32 ddc_timeout; /* 0x524 */ + u8 res6[0x18]; /* 0x528 */ + u32 ddc_dbg; /* 0x540 */ + u8 res7[0x3c]; /* 0x544 */ + u32 ddc_fifo_data; /* 0x580 */ +#endif };
/* @@ -182,6 +224,49 @@ struct sunxi_hdmi_reg { #define SUNXI_HDMI_PLL_DBG0_PLL3 (0 << 21) #define SUNXI_HDMI_PLL_DBG0_PLL7 (1 << 21)
+#ifdef CONFIG_MACH_SUN6I +#define SUNXI_HMDI_DDC_CTRL_ENABLE (1 << 0) +#define SUNXI_HMDI_DDC_CTRL_SCL_ENABLE (1 << 4) +#define SUNXI_HMDI_DDC_CTRL_SDA_ENABLE (1 << 6) +#define SUNXI_HMDI_DDC_CTRL_START (1 << 27) +#define SUNXI_HMDI_DDC_CTRL_RESET (1 << 31) +#else +#define SUNXI_HMDI_DDC_CTRL_RESET (1 << 0) +/* sun4i / sun5i / sun7i do not have a separate line_ctrl reg */ +#define SUNXI_HMDI_DDC_CTRL_SDA_ENABLE 0 +#define SUNXI_HMDI_DDC_CTRL_SCL_ENABLE 0 +#define SUNXI_HMDI_DDC_CTRL_START (1 << 30) +#define SUNXI_HMDI_DDC_CTRL_ENABLE (1 << 31) +#endif + +#ifdef CONFIG_MACH_SUN6I +#define SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR (0xa0 << 0) +#else +#define SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR (0x50 << 0) +#endif +#define SUNXI_HMDI_DDC_ADDR_OFFSET(n) (((n) & 0xff) << 8) +#define SUNXI_HMDI_DDC_ADDR_EDDC_ADDR (0x60 << 16) +#define SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(n) ((n) << 24) + +#ifdef CONFIG_MACH_SUN6I +#define SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR (1 << 15) +#else +#define SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR (1 << 31) +#endif + +#define SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ 6 +#define SUNXI_HDMI_DDC_CMND_IMPLICIT_EDDC_READ 7 + +#ifdef CONFIG_MACH_SUN6I +#define SUNXI_HDMI_DDC_CLOCK 0x61 +#else +/* N = 5,M=1 Fscl= Ftmds/2/10/2^N/(M+1) */ +#define SUNXI_HDMI_DDC_CLOCK 0x0d +#endif + +#define SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE (1 << 8) +#define SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE (1 << 9) + int sunxi_simplefb_setup(void *blob);
#endif /* _SUNXI_DISPLAY_H */ diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 18fa83d..0997740 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -13,6 +13,7 @@ #include <asm/arch/display.h> #include <asm/global_data.h> #include <asm/io.h> +#include <errno.h> #include <fdtdec.h> #include <fdt_support.h> #include <video_fb.h> @@ -25,6 +26,22 @@ struct sunxi_display { bool enabled; } sunxi_display;
+/* + * Wait up to 200ms for value to be set in given part of reg. + */ +static int await_completion(u32 *reg, u32 mask, u32 val) +{ + unsigned long tmo = timer_get_us() + 200000; + + while ((readl(reg) & mask) != val) { + if (timer_get_us() > tmo) { + printf("DDC: timeout reading EDID\n"); + return -ETIME; + } + } + return 0; +} + static int sunxi_hdmi_hpd_detect(void) { struct sunxi_ccm_reg * const ccm = @@ -72,6 +89,133 @@ static void sunxi_hdmi_shutdown(void) clock_set_pll3(0); }
+static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n) +{ + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + + setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR); + writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) | + SUNXI_HMDI_DDC_ADDR_EDDC_ADDR | + SUNXI_HMDI_DDC_ADDR_OFFSET(offset) | + SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr); +#ifndef CONFIG_MACH_SUN6I + writel(n, &hdmi->ddc_byte_count); + writel(cmnd, &hdmi->ddc_cmnd); +#else + writel(n << 16 | cmnd, &hdmi->ddc_cmnd); +#endif + setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START); + + return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0); +} + +static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count) +{ + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + int i, n; + + while (count > 0) { + if (count > 16) + n = 16; + else + n = count; + + if (sunxi_hdmi_ddc_do_command( + SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ, + offset, n)) + return -ETIME; + + for (i = 0; i < n; i++) + *buf++ = readb(&hdmi->ddc_fifo_data); + + offset += n; + count -= n; + } + + return 0; +} + +static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode) +{ + struct edid1_info edid1; + struct edid_detailed_timing *t = + (struct edid_detailed_timing *)edid1.monitor_details.timing; + struct sunxi_hdmi_reg * const hdmi = + (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int i, r, retries = 2; + + /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */ + writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE, + &hdmi->pad_ctrl1); + writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15), + &hdmi->pll_ctrl); + writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0); + + /* Reset i2c controller */ + setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE); + writel(SUNXI_HMDI_DDC_CTRL_ENABLE | + SUNXI_HMDI_DDC_CTRL_SDA_ENABLE | + SUNXI_HMDI_DDC_CTRL_SCL_ENABLE | + SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl); + if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0)) + return -EIO; + + writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock); +#ifndef CONFIG_MACH_SUN6I + writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE | + SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl); +#endif + + do { + r = sunxi_hdmi_ddc_read(0, (u8 *)&edid1, 128); + if (r) + continue; + r = edid_check_checksum((u8 *)&edid1); + if (r) { + printf("EDID: checksum error%s\n", + retries ? ", retrying" : ""); + } + } while (r && retries--); + + /* Disable DDC engine, no longer needed */ + clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE); + clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE); + + if (r) + return r; + + r = edid_check_info(&edid1); + if (r) { + printf("EDID: invalid EDID data\n"); + return -EINVAL; + } + + /* We want version 1.3 or 1.2 with detailed timing info */ + if (edid1.version != 1 || (edid1.revision < 3 && + !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) { + printf("EDID: unsupported version %d.%d\n", + edid1.version, edid1.revision); + return -EINVAL; + } + + /* Take the first usable detailed timing */ + for (i = 0; i < 4; i++, t++) { + r = video_edid_dtd_to_ctfb_res_modes(t, mode); + if (r == 0) + break; + } + if (i == 4) { + printf("EDID: no usable detailed timing found\n"); + return -ENOENT; + } + + return 0; +} + /* * This is the entity that mixes and matches the different layers and inputs. * Allwinner calls it the back-end, but i like composer better. @@ -363,9 +507,10 @@ void *video_hw_init(void) { static GraphicDevice *graphic_device = &sunxi_display.graphic_device; const struct ctfb_res_modes *mode; + struct ctfb_res_modes edid_mode; const char *options; unsigned int depth; - int ret, hpd; + int ret, hpd, edid;
memset(&sunxi_display, 0, sizeof(struct sunxi_display));
@@ -375,6 +520,7 @@ void *video_hw_init(void)
video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode, &depth, &options); hpd = video_get_option_int(options, "hpd", 1); + edid = video_get_option_int(options, "edid", 1);
/* Always call hdp_detect, as it also enables various clocks, etc. */ ret = sunxi_hdmi_hpd_detect(); @@ -385,6 +531,12 @@ void *video_hw_init(void) if (ret) printf("HDMI connected: ");
+ /* Check edid if requested and we've a cable plugged in */ + if (edid && ret) { + if (sunxi_hdmi_edid_get_mode(&edid_mode) == 0) + mode = &edid_mode; + } + if (mode->vmode != FB_VMODE_NONINTERLACED) { printf("Only non-interlaced modes supported, falling back to 1024x768\n"); mode = &res_mode_init[RES_MODE_1024x768]; diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index 77965f7..30b7ee4 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -213,6 +213,7 @@ #define CONFIG_VIDEO_SW_CURSOR #define CONFIG_VIDEO_LOGO #define CONFIG_VIDEO_STD_TIMINGS +#define CONFIG_I2C_EDID
/* allow both serial and cfb console. */ #define CONFIG_CONSOLE_MUX

On Fri, 2014-12-19 at 18:10 +0100, Hans de Goede wrote:
Add DDC & EDID support and use it to automatically select the native mode of the attached monitor. This can be disabled by adding edid=0 as option to the video-mode env. variable.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

On Fri, 19 Dec 2014 18:10:41 +0100 Hans de Goede hdegoede@redhat.com wrote:
Add DDC & EDID support and use it to automatically select the native mode of the attached monitor. This can be disabled by adding edid=0 as option to the video-mode env. variable.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Anatolij Gustschin agust@denx.de
participants (3)
-
Anatolij Gustschin
-
Hans de Goede
-
Ian Campbell