diff options
Diffstat (limited to 'drivers/video')
44 files changed, 2047 insertions, 318 deletions
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index fe1cd0148e13..ba97efc3bf70 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -77,18 +77,22 @@ config DUMMY_CONSOLE config DUMMY_CONSOLE_COLUMNS int "Initial number of console screen columns" - depends on PARISC && DUMMY_CONSOLE - default "160" + depends on DUMMY_CONSOLE && !ARM + default 160 if PARISC + default 80 help - The default value is 160, which should fit a 1280x1024 monitor. + On PA-RISC, the default value is 160, which should fit a 1280x1024 + monitor. Select 80 if you use a 640x480 resolution by default. config DUMMY_CONSOLE_ROWS int "Initial number of console screen rows" - depends on PARISC && DUMMY_CONSOLE - default "64" + depends on DUMMY_CONSOLE && !ARM + default 64 if PARISC + default 25 help - The default value is 64, which should fit a 1280x1024 monitor. + On PA-RISC, the default value is 64, which should fit a 1280x1024 + monitor. Select 25 if you use a 640x480 resolution by default. config FRAMEBUFFER_CONSOLE diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 40bec8d64b0a..0efc52f11ad0 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -20,13 +20,10 @@ #if defined(__arm__) #define DUMMY_COLUMNS screen_info.orig_video_cols #define DUMMY_ROWS screen_info.orig_video_lines -#elif defined(__hppa__) +#else /* set by Kconfig. Use 80x25 for 640x480 and 160x64 for 1280x1024 */ #define DUMMY_COLUMNS CONFIG_DUMMY_CONSOLE_COLUMNS #define DUMMY_ROWS CONFIG_DUMMY_CONSOLE_ROWS -#else -#define DUMMY_COLUMNS 80 -#define DUMMY_ROWS 25 #endif static const char *dummycon_startup(void) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index ea437245562e..b97210671a81 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -146,9 +146,6 @@ static const struct consw fb_con; static int fbcon_set_origin(struct vc_data *); -#define CURSOR_DRAW_DELAY (1) - -static int vbl_cursor_cnt; static int fbcon_cursor_noblink; #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) @@ -1329,7 +1326,6 @@ static void fbcon_cursor(struct vc_data *vc, int mode) ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); - vbl_cursor_cnt = CURSOR_DRAW_DELAY; } static int scrollback_phys_max = 0; diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 4916c97216f8..b3dd417b4719 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1530,13 +1530,11 @@ config FB_SIS_315 config FB_VIA tristate "VIA UniChrome (Pro) and Chrome9 display support" - depends on FB && PCI && X86 + depends on FB && PCI && X86 && GPIOLIB && I2C select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select I2C_ALGOBIT - select I2C - select GPIOLIB help This is the frame buffer device driver for Graphics chips of VIA UniChrome (Pro) Family (CLE266,PM800/CN400,P4M800CE/P4M800Pro/ @@ -2151,7 +2149,6 @@ config FB_PS3 select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT select FB_SYS_FOPS - select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE ---help--- Include support for the virtual frame buffer in the PS3 platform. diff --git a/drivers/video/fbdev/atafb.c b/drivers/video/fbdev/atafb.c index 4953b657635e..cb9ee2556850 100644 --- a/drivers/video/fbdev/atafb.c +++ b/drivers/video/fbdev/atafb.c @@ -3118,8 +3118,7 @@ int __init atafb_init(void) printk("atafb_init: initializing Falcon hw\n"); fbhw = &falcon_switch; atafb_ops.fb_setcolreg = &falcon_setcolreg; - error = request_irq(IRQ_AUTO_4, falcon_vbl_switcher, - IRQ_TYPE_PRIO, + error = request_irq(IRQ_AUTO_4, falcon_vbl_switcher, 0, "framebuffer:modeswitch", falcon_vbl_switcher); if (error) diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 37ec09b3fffd..8789e487b96e 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -3948,7 +3948,7 @@ static struct notifier_block atyfb_reboot_notifier = { .notifier_call = atyfb_reboot_notify, }; -static const struct dmi_system_id atyfb_reboot_ids[] = { +static const struct dmi_system_id atyfb_reboot_ids[] __initconst = { { .ident = "HP OmniBook 500", .matches = { @@ -3960,6 +3960,7 @@ static const struct dmi_system_id atyfb_reboot_ids[] = { { } }; +static bool registered_notifier = false; static int __init atyfb_init(void) { @@ -3982,15 +3983,17 @@ static int __init atyfb_init(void) if (err1 && err2) return -ENODEV; - if (dmi_check_system(atyfb_reboot_ids)) + if (dmi_check_system(atyfb_reboot_ids)) { register_reboot_notifier(&atyfb_reboot_notifier); + registered_notifier = true; + } return 0; } static void __exit atyfb_exit(void) { - if (dmi_check_system(atyfb_reboot_ids)) + if (registered_notifier) unregister_reboot_notifier(&atyfb_reboot_notifier); #ifdef CONFIG_PCI diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c index 1c29bd19e3d5..0e5fde1d3ffb 100644 --- a/drivers/video/fbdev/broadsheetfb.c +++ b/drivers/video/fbdev/broadsheetfb.c @@ -636,7 +636,7 @@ static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, err = broadsheet_spiflash_read_range(par, start_sector_addr, data_start_addr, sector_buffer); if (err) - return err; + goto out; } /* now we copy our data into the right place in the sector buffer */ @@ -657,7 +657,7 @@ static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, err = broadsheet_spiflash_read_range(par, tail_start_addr, tail_len, sector_buffer + tail_start_addr); if (err) - return err; + goto out; } /* if we got here we have the full sector that we want to rewrite. */ @@ -665,11 +665,13 @@ static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, /* first erase the sector */ err = broadsheet_spiflash_erase_sector(par, start_sector_addr); if (err) - return err; + goto out; /* now write it */ err = broadsheet_spiflash_write_sector(par, start_sector_addr, sector_buffer, sector_size); +out: + kfree(sector_buffer); return err; } diff --git a/drivers/video/fbdev/core/fbcvt.c b/drivers/video/fbdev/core/fbcvt.c index 7cb715dfc0e1..55d2bd0ce5c0 100644 --- a/drivers/video/fbdev/core/fbcvt.c +++ b/drivers/video/fbdev/core/fbcvt.c @@ -369,9 +369,9 @@ int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb) cvt.h_back_porch = cvt.hblank/2 + cvt.h_margin; cvt.h_front_porch = cvt.hblank - cvt.hsync - cvt.h_back_porch + 2 * cvt.h_margin; - cvt.v_back_porch = 3 + cvt.v_margin; - cvt.v_front_porch = cvt.vtotal - cvt.yres/cvt.interlace - - cvt.v_back_porch - cvt.vsync; + cvt.v_front_porch = 3 + cvt.v_margin; + cvt.v_back_porch = cvt.vtotal - cvt.yres/cvt.interlace - + cvt.v_front_porch - cvt.vsync; fb_cvt_print_name(&cvt); fb_cvt_convert_to_mode(&cvt, mode); diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index 5b0e313849bd..95338593ebf4 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -496,56 +496,71 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode) } static int get_std_timing(unsigned char *block, struct fb_videomode *mode, - int ver, int rev) + int ver, int rev, const struct fb_monspecs *specs) { - int xres, yres = 0, refresh, ratio, i; - - xres = (block[0] + 31) * 8; - if (xres <= 256) - return 0; + int i; - ratio = (block[1] & 0xc0) >> 6; - switch (ratio) { - case 0: - /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */ - if (ver < 1 || (ver == 1 && rev < 3)) - yres = xres; - else - yres = (xres * 10)/16; - break; - case 1: - yres = (xres * 3)/4; - break; - case 2: - yres = (xres * 4)/5; - break; - case 3: - yres = (xres * 9)/16; - break; + for (i = 0; i < DMT_SIZE; i++) { + u32 std_2byte_code = block[0] << 8 | block[1]; + if (std_2byte_code == dmt_modes[i].std_2byte_code) + break; } - refresh = (block[1] & 0x3f) + 60; - - DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh); - for (i = 0; i < VESA_MODEDB_SIZE; i++) { - if (vesa_modes[i].xres == xres && - vesa_modes[i].yres == yres && - vesa_modes[i].refresh == refresh) { - *mode = vesa_modes[i]; - mode->flag |= FB_MODE_IS_STANDARD; - return 1; + + if (i < DMT_SIZE && dmt_modes[i].mode) { + /* DMT mode found */ + *mode = *dmt_modes[i].mode; + mode->flag |= FB_MODE_IS_STANDARD; + DPRINTK(" DMT id=%d\n", dmt_modes[i].dmt_id); + + } else { + int xres, yres = 0, refresh, ratio; + + xres = (block[0] + 31) * 8; + if (xres <= 256) + return 0; + + ratio = (block[1] & 0xc0) >> 6; + switch (ratio) { + case 0: + /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */ + if (ver < 1 || (ver == 1 && rev < 3)) + yres = xres; + else + yres = (xres * 10)/16; + break; + case 1: + yres = (xres * 3)/4; + break; + case 2: + yres = (xres * 4)/5; + break; + case 3: + yres = (xres * 9)/16; + break; } + refresh = (block[1] & 0x3f) + 60; + DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh); + + calc_mode_timings(xres, yres, refresh, mode); } - calc_mode_timings(xres, yres, refresh, mode); + + /* Check the mode we got is within valid spec of the monitor */ + if (specs && specs->dclkmax + && PICOS2KHZ(mode->pixclock) * 1000 > specs->dclkmax) { + DPRINTK(" mode exceed max DCLK\n"); + return 0; + } + return 1; } -static int get_dst_timing(unsigned char *block, - struct fb_videomode *mode, int ver, int rev) +static int get_dst_timing(unsigned char *block, struct fb_videomode *mode, + int ver, int rev, const struct fb_monspecs *specs) { int j, num = 0; for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE) - num += get_std_timing(block, &mode[num], ver, rev); + num += get_std_timing(block, &mode[num], ver, rev, specs); return num; } @@ -601,7 +616,8 @@ static void get_detailed_timing(unsigned char *block, * This function builds a mode database using the contents of the EDID * data */ -static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) +static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize, + const struct fb_monspecs *specs) { struct fb_videomode *mode, *m; unsigned char *block; @@ -643,12 +659,13 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) DPRINTK(" Standard Timings\n"); block = edid + STD_TIMING_DESCRIPTIONS_START; for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE) - num += get_std_timing(block, &mode[num], ver, rev); + num += get_std_timing(block, &mode[num], ver, rev, specs); block = edid + DETAILED_TIMING_DESCRIPTIONS_START; for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa) - num += get_dst_timing(block + 5, &mode[num], ver, rev); + num += get_dst_timing(block + 5, &mode[num], + ver, rev, specs); } /* Yikes, EDID data is totally useless */ @@ -707,7 +724,7 @@ static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) int num_modes, hz, hscan, pixclock; int vtotal, htotal; - modes = fb_create_modedb(edid, &num_modes); + modes = fb_create_modedb(edid, &num_modes, specs); if (!modes) { DPRINTK("None Available\n"); return 1; @@ -964,7 +981,7 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) DPRINTK(" Display Characteristics:\n"); get_monspecs(edid, specs); - specs->modedb = fb_create_modedb(edid, &specs->modedb_len); + specs->modedb = fb_create_modedb(edid, &specs->modedb_len, specs); /* * Workaround for buggy EDIDs that sets that the first diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 388f7971494b..7d07cf824b64 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -468,8 +468,119 @@ const struct fb_videomode vesa_modes[] = { /* 33 1920x1440-75 VESA */ { NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3, FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 34 1920x1200-60 RB VESA */ + { NULL, 60, 1920, 1200, 6493, 80, 48, 26, 3, 32, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 35 1920x1200-60 VESA */ + { NULL, 60, 1920, 1200, 5174, 336, 136, 36, 3, 200, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 36 1920x1200-75 VESA */ + { NULL, 75, 1920, 1200, 4077, 344, 136, 46, 3, 208, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 37 1920x1200-85 VESA */ + { NULL, 85, 1920, 1200, 3555, 352, 144, 53, 3, 208, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 38 2560x1600-60 RB VESA */ + { NULL, 60, 2560, 1600, 3724, 80, 48, 37, 3, 32, 6, + FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 39 2560x1600-60 VESA */ + { NULL, 60, 2560, 1600, 2869, 472, 192, 49, 3, 280, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 40 2560x1600-75 VESA */ + { NULL, 75, 2560, 1600, 2256, 488, 208, 63, 3, 280, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 41 2560x1600-85 VESA */ + { NULL, 85, 2560, 1600, 1979, 488, 208, 73, 3, 280, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 42 2560x1600-120 RB VESA */ + { NULL, 120, 2560, 1600, 1809, 80, 48, 85, 3, 32, 6, + FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, }; EXPORT_SYMBOL(vesa_modes); + +const struct dmt_videomode dmt_modes[DMT_SIZE] = { + { 0x01, 0x0000, 0x000000, &vesa_modes[0] }, + { 0x02, 0x3119, 0x000000, &vesa_modes[1] }, + { 0x03, 0x0000, 0x000000, &vesa_modes[2] }, + { 0x04, 0x3140, 0x000000, &vesa_modes[3] }, + { 0x05, 0x314c, 0x000000, &vesa_modes[4] }, + { 0x06, 0x314f, 0x000000, &vesa_modes[5] }, + { 0x07, 0x3159, 0x000000, &vesa_modes[6] }, + { 0x08, 0x0000, 0x000000, &vesa_modes[7] }, + { 0x09, 0x4540, 0x000000, &vesa_modes[8] }, + { 0x0a, 0x454c, 0x000000, &vesa_modes[9] }, + { 0x0b, 0x454f, 0x000000, &vesa_modes[10] }, + { 0x0c, 0x4559, 0x000000, &vesa_modes[11] }, + { 0x0d, 0x0000, 0x000000, NULL }, + { 0x0e, 0x0000, 0x000000, NULL }, + { 0x0f, 0x0000, 0x000000, &vesa_modes[12] }, + { 0x10, 0x6140, 0x000000, &vesa_modes[13] }, + { 0x11, 0x614a, 0x000000, &vesa_modes[14] }, + { 0x12, 0x614f, 0x000000, &vesa_modes[15] }, + { 0x13, 0x6159, 0x000000, &vesa_modes[16] }, + { 0x14, 0x0000, 0x000000, NULL }, + { 0x15, 0x714f, 0x000000, &vesa_modes[17] }, + { 0x16, 0x0000, 0x7f1c21, NULL }, + { 0x17, 0x0000, 0x7f1c28, NULL }, + { 0x18, 0x0000, 0x7f1c44, NULL }, + { 0x19, 0x0000, 0x7f1c62, NULL }, + { 0x1a, 0x0000, 0x000000, NULL }, + { 0x1b, 0x0000, 0x8f1821, NULL }, + { 0x1c, 0x8100, 0x8f1828, NULL }, + { 0x1d, 0x810f, 0x8f1844, NULL }, + { 0x1e, 0x8119, 0x8f1862, NULL }, + { 0x1f, 0x0000, 0x000000, NULL }, + { 0x20, 0x8140, 0x000000, &vesa_modes[18] }, + { 0x21, 0x8159, 0x000000, &vesa_modes[19] }, + { 0x22, 0x0000, 0x000000, NULL }, + { 0x23, 0x8180, 0x000000, &vesa_modes[20] }, + { 0x24, 0x818f, 0x000000, &vesa_modes[21] }, + { 0x25, 0x8199, 0x000000, &vesa_modes[22] }, + { 0x26, 0x0000, 0x000000, NULL }, + { 0x27, 0x0000, 0x000000, NULL }, + { 0x28, 0x0000, 0x000000, NULL }, + { 0x29, 0x0000, 0x0c2021, NULL }, + { 0x2a, 0x9040, 0x0c2028, NULL }, + { 0x2b, 0x904f, 0x0c2044, NULL }, + { 0x2c, 0x9059, 0x0c2062, NULL }, + { 0x2d, 0x0000, 0x000000, NULL }, + { 0x2e, 0x9500, 0xc11821, NULL }, + { 0x2f, 0x9500, 0xc11828, NULL }, + { 0x30, 0x950f, 0xc11844, NULL }, + { 0x31, 0x9519, 0xc11868, NULL }, + { 0x32, 0x0000, 0x000000, NULL }, + { 0x33, 0xa940, 0x000000, &vesa_modes[23] }, + { 0x34, 0xa945, 0x000000, &vesa_modes[24] }, + { 0x35, 0xa94a, 0x000000, &vesa_modes[25] }, + { 0x36, 0xa94f, 0x000000, &vesa_modes[26] }, + { 0x37, 0xa959, 0x000000, &vesa_modes[27] }, + { 0x38, 0x0000, 0x000000, NULL }, + { 0x39, 0x0000, 0x0c2821, NULL }, + { 0x3a, 0xb300, 0x0c2828, NULL }, + { 0x3b, 0xb30f, 0x0c2844, NULL }, + { 0x3c, 0xb319, 0x0c2868, NULL }, + { 0x3d, 0x0000, 0x000000, NULL }, + { 0x3e, 0xc140, 0x000000, &vesa_modes[28] }, + { 0x3f, 0xc14f, 0x000000, &vesa_modes[29] }, + { 0x40, 0x0000, 0x000000, NULL}, + { 0x41, 0xc940, 0x000000, &vesa_modes[30] }, + { 0x42, 0xc94f, 0x000000, &vesa_modes[31] }, + { 0x43, 0x0000, 0x000000, NULL }, + { 0x44, 0x0000, 0x572821, &vesa_modes[34] }, + { 0x45, 0xd100, 0x572828, &vesa_modes[35] }, + { 0x46, 0xd10f, 0x572844, &vesa_modes[36] }, + { 0x47, 0xd119, 0x572862, &vesa_modes[37] }, + { 0x48, 0x0000, 0x000000, NULL }, + { 0x49, 0xd140, 0x000000, &vesa_modes[32] }, + { 0x4a, 0xd14f, 0x000000, &vesa_modes[33] }, + { 0x4b, 0x0000, 0x000000, NULL }, + { 0x4c, 0x0000, 0x1f3821, &vesa_modes[38] }, + { 0x4d, 0x0000, 0x1f3828, &vesa_modes[39] }, + { 0x4e, 0x0000, 0x1f3844, &vesa_modes[40] }, + { 0x4f, 0x0000, 0x1f3862, &vesa_modes[41] }, + { 0x50, 0x0000, 0x000000, &vesa_modes[42] }, +}; +EXPORT_SYMBOL(dmt_modes); #endif /* CONFIG_FB_MODE_HELPERS */ /** diff --git a/drivers/video/fbdev/core/syscopyarea.c b/drivers/video/fbdev/core/syscopyarea.c index 844a32fd38ed..c1eda3190968 100644 --- a/drivers/video/fbdev/core/syscopyarea.c +++ b/drivers/video/fbdev/core/syscopyarea.c @@ -25,8 +25,8 @@ */ static void -bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, - const unsigned long *src, int src_idx, int bits, unsigned n) +bitcpy(struct fb_info *p, unsigned long *dst, unsigned dst_idx, + const unsigned long *src, unsigned src_idx, int bits, unsigned n) { unsigned long first, last; int const shift = dst_idx-src_idx; @@ -86,15 +86,15 @@ bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, first &= last; if (shift > 0) { /* Single source word */ - *dst = comp(*src >> right, *dst, first); + *dst = comp(*src << left, *dst, first); } else if (src_idx+n <= bits) { /* Single source word */ - *dst = comp(*src << left, *dst, first); + *dst = comp(*src >> right, *dst, first); } else { /* 2 source words */ d0 = *src++; d1 = *src; - *dst = comp(d0 << left | d1 >> right, *dst, + *dst = comp(d0 >> right | d1 << left, *dst, first); } } else { @@ -109,13 +109,14 @@ bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, /* Leading bits */ if (shift > 0) { /* Single source word */ - *dst = comp(d0 >> right, *dst, first); + *dst = comp(d0 << left, *dst, first); dst++; n -= bits - dst_idx; } else { /* 2 source words */ d1 = *src++; - *dst = comp(d0 << left | *dst >> right, *dst, first); + *dst = comp(d0 >> right | d1 << left, *dst, + first); d0 = d1; dst++; n -= bits - dst_idx; @@ -126,36 +127,36 @@ bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, n /= bits; while (n >= 4) { d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; n -= 4; } while (n--) { d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; } /* Trailing bits */ - if (last) { - if (m <= right) { + if (m) { + if (m <= bits - right) { /* Single source word */ - *dst = comp(d0 << left, *dst, last); + d0 >>= right; } else { /* 2 source words */ d1 = *src; - *dst = comp(d0 << left | d1 >> right, - *dst, last); + d0 = d0 >> right | d1 << left; } + *dst = comp(d0, *dst, last); } } } @@ -166,40 +167,35 @@ bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, */ static void -bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx, - const unsigned long *src, int src_idx, int bits, unsigned n) +bitcpy_rev(struct fb_info *p, unsigned long *dst, unsigned dst_idx, + const unsigned long *src, unsigned src_idx, unsigned bits, + unsigned n) { unsigned long first, last; int shift; - dst += (n-1)/bits; - src += (n-1)/bits; - if ((n-1) % bits) { - dst_idx += (n-1) % bits; - dst += dst_idx >> (ffs(bits) - 1); - dst_idx &= bits - 1; - src_idx += (n-1) % bits; - src += src_idx >> (ffs(bits) - 1); - src_idx &= bits - 1; - } + dst += (dst_idx + n - 1) / bits; + src += (src_idx + n - 1) / bits; + dst_idx = (dst_idx + n - 1) % bits; + src_idx = (src_idx + n - 1) % bits; shift = dst_idx-src_idx; - first = FB_SHIFT_LOW(p, ~0UL, bits - 1 - dst_idx); - last = ~(FB_SHIFT_LOW(p, ~0UL, bits - 1 - ((dst_idx-n) % bits))); + first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits); + last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits); if (!shift) { /* Same alignment for source and dest */ if ((unsigned long)dst_idx+1 >= n) { /* Single word */ - if (last) - first &= last; - *dst = comp(*src, *dst, first); + if (first) + last &= first; + *dst = comp(*src, *dst, last); } else { /* Multiple destination words */ /* Leading bits */ - if (first != ~0UL) { + if (first) { *dst = comp(*src, *dst, first); dst--; src--; @@ -222,29 +218,29 @@ bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx, while (n--) *dst-- = *src--; /* Trailing bits */ - if (last) + if (last != -1UL) *dst = comp(*src, *dst, last); } } else { /* Different alignment for source and dest */ - int const left = -shift & (bits-1); - int const right = shift & (bits-1); + int const left = shift & (bits-1); + int const right = -shift & (bits-1); if ((unsigned long)dst_idx+1 >= n) { /* Single destination word */ - if (last) - first &= last; + if (first) + last &= first; if (shift < 0) { /* Single source word */ - *dst = comp(*src << left, *dst, first); + *dst = comp(*src >> right, *dst, last); } else if (1+(unsigned long)src_idx >= n) { /* Single source word */ - *dst = comp(*src >> right, *dst, first); + *dst = comp(*src << left, *dst, last); } else { /* 2 source words */ - *dst = comp(*src >> right | *(src-1) << left, - *dst, first); + *dst = comp(*src << left | *(src-1) >> right, + *dst, last); } } else { /* Multiple destination words */ @@ -261,14 +257,18 @@ bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx, /* Leading bits */ if (shift < 0) { /* Single source word */ - *dst = comp(d0 << left, *dst, first); + d1 = d0; + d0 >>= right; } else { /* 2 source words */ d1 = *src--; - *dst = comp(d0 >> right | d1 << left, *dst, - first); - d0 = d1; + d0 = d0 << left | d1 >> right; } + if (!first) + *dst = d0; + else + *dst = comp(d0, *dst, first); + d0 = d1; dst--; n -= dst_idx+1; @@ -277,36 +277,36 @@ bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx, n /= bits; while (n >= 4) { d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; n -= 4; } while (n--) { d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; } /* Trailing bits */ - if (last) { - if (m <= left) { + if (m) { + if (m <= bits - left) { /* Single source word */ - *dst = comp(d0 >> right, *dst, last); + d0 <<= left; } else { /* 2 source words */ d1 = *src; - *dst = comp(d0 >> right | d1 << left, - *dst, last); + d0 = d0 << left | d1 >> right; } + *dst = comp(d0, *dst, last); } } } @@ -317,9 +317,9 @@ void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area) u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy; u32 height = area->height, width = area->width; unsigned long const bits_per_line = p->fix.line_length*8u; - unsigned long *dst = NULL, *src = NULL; + unsigned long *base = NULL; int bits = BITS_PER_LONG, bytes = bits >> 3; - int dst_idx = 0, src_idx = 0, rev_copy = 0; + unsigned dst_idx = 0, src_idx = 0, rev_copy = 0; if (p->state != FBINFO_STATE_RUNNING) return; @@ -334,8 +334,7 @@ void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area) /* split the base of the framebuffer into a long-aligned address and the index of the first bit */ - dst = src = (unsigned long *)((unsigned long)p->screen_base & - ~(bytes-1)); + base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1)); dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1)); /* add offset of source and target area */ dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel; @@ -348,20 +347,14 @@ void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area) while (height--) { dst_idx -= bits_per_line; src_idx -= bits_per_line; - dst += dst_idx >> (ffs(bits) - 1); - dst_idx &= (bytes - 1); - src += src_idx >> (ffs(bits) - 1); - src_idx &= (bytes - 1); - bitcpy_rev(p, dst, dst_idx, src, src_idx, bits, + bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits, + base + (src_idx / bits), src_idx % bits, bits, width*p->var.bits_per_pixel); } } else { while (height--) { - dst += dst_idx >> (ffs(bits) - 1); - dst_idx &= (bytes - 1); - src += src_idx >> (ffs(bits) - 1); - src_idx &= (bytes - 1); - bitcpy(p, dst, dst_idx, src, src_idx, bits, + bitcpy(p, base + (dst_idx / bits), dst_idx % bits, + base + (src_idx / bits), src_idx % bits, bits, width*p->var.bits_per_pixel); dst_idx += bits_per_line; src_idx += bits_per_line; diff --git a/drivers/video/fbdev/geode/gx1fb_core.c b/drivers/video/fbdev/geode/gx1fb_core.c index 2794ba11f332..9bee8744c438 100644 --- a/drivers/video/fbdev/geode/gx1fb_core.c +++ b/drivers/video/fbdev/geode/gx1fb_core.c @@ -374,10 +374,8 @@ static int gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id *id) release_mem_region(gx1_gx_base() + 0x8300, 0x100); } - if (info) { - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); return ret; } diff --git a/drivers/video/fbdev/geode/gxfb_core.c b/drivers/video/fbdev/geode/gxfb_core.c index 1790f14bab15..124d7c7e2d14 100644 --- a/drivers/video/fbdev/geode/gxfb_core.c +++ b/drivers/video/fbdev/geode/gxfb_core.c @@ -444,10 +444,8 @@ static int gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_release_region(pdev, 1); } - if (info) { - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); return ret; } diff --git a/drivers/video/fbdev/geode/lxfb_core.c b/drivers/video/fbdev/geode/lxfb_core.c index 9e1d19d673a1..138da6cb6cbc 100644 --- a/drivers/video/fbdev/geode/lxfb_core.c +++ b/drivers/video/fbdev/geode/lxfb_core.c @@ -577,10 +577,8 @@ err: pci_release_region(pdev, 3); } - if (info) { - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); return ret; } diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c index 5ff9fe2116a4..15d3ccff2965 100644 --- a/drivers/video/fbdev/hgafb.c +++ b/drivers/video/fbdev/hgafb.c @@ -417,8 +417,7 @@ static int hgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { if (var->vmode & FB_VMODE_YWRAP) { - if (var->yoffset < 0 || - var->yoffset >= info->var.yres_virtual || + if (var->yoffset >= info->var.yres_virtual || var->xoffset) return -EINVAL; } else { diff --git a/drivers/video/fbdev/mmp/Makefile b/drivers/video/fbdev/mmp/Makefile index a014cb358bf8..924dd0930cc7 100644 --- a/drivers/video/fbdev/mmp/Makefile +++ b/drivers/video/fbdev/mmp/Makefile @@ -1 +1,3 @@ -obj-y += core.o hw/ panel/ fb/ +obj-$(CONFIG_MMP_DISP) += mmp_disp.o hw/ panel/ fb/ + +mmp_disp-y += core.o diff --git a/drivers/video/fbdev/mmp/fb/Kconfig b/drivers/video/fbdev/mmp/fb/Kconfig index 9b0141f105f5..985e1a7cd254 100644 --- a/drivers/video/fbdev/mmp/fb/Kconfig +++ b/drivers/video/fbdev/mmp/fb/Kconfig @@ -1,7 +1,7 @@ if MMP_DISP config MMP_FB - bool "fb driver for Marvell MMP Display Subsystem" + tristate "fb driver for Marvell MMP Display Subsystem" depends on FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA diff --git a/drivers/video/fbdev/ocfb.c b/drivers/video/fbdev/ocfb.c index 7f9dc9bec309..de9819660ca0 100644 --- a/drivers/video/fbdev/ocfb.c +++ b/drivers/video/fbdev/ocfb.c @@ -61,7 +61,7 @@ struct ocfb_dev { /* flag indicating whether the regs are little endian accessed */ int little_endian; /* Physical and virtual addresses of framebuffer */ - phys_addr_t fb_phys; + dma_addr_t fb_phys; void __iomem *fb_virt; u32 pseudo_palette[PALETTE_SIZE]; }; diff --git a/drivers/video/fbdev/omap2/displays-new/Kconfig b/drivers/video/fbdev/omap2/displays-new/Kconfig index e6cfc38160d3..574710141a61 100644 --- a/drivers/video/fbdev/omap2/displays-new/Kconfig +++ b/drivers/video/fbdev/omap2/displays-new/Kconfig @@ -1,6 +1,12 @@ menu "OMAP Display Device Drivers (new device model)" depends on OMAP2_DSS +config DISPLAY_ENCODER_OPA362 + tristate "OPA362 external analog amplifier" + help + Driver for OPA362 external analog TV amplifier controlled + through a GPIO. + config DISPLAY_ENCODER_TFP410 tristate "TFP410 DPI to DVI Encoder" help diff --git a/drivers/video/fbdev/omap2/displays-new/Makefile b/drivers/video/fbdev/omap2/displays-new/Makefile index 0323a8a1c682..9aa176bfbf2e 100644 --- a/drivers/video/fbdev/omap2/displays-new/Makefile +++ b/drivers/video/fbdev/omap2/displays-new/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_DISPLAY_ENCODER_OPA362) += encoder-opa362.o obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o diff --git a/drivers/video/fbdev/omap2/displays-new/connector-analog-tv.c b/drivers/video/fbdev/omap2/displays-new/connector-analog-tv.c index 9a2b5ce58545..8511c648a15c 100644 --- a/drivers/video/fbdev/omap2/displays-new/connector-analog-tv.c +++ b/drivers/video/fbdev/omap2/displays-new/connector-analog-tv.c @@ -208,7 +208,7 @@ static int tvc_probe_pdata(struct platform_device *pdev) ddata->in = in; ddata->connector_type = pdata->connector_type; - ddata->invert_polarity = ddata->invert_polarity; + ddata->invert_polarity = pdata->invert_polarity; dssdev = &ddata->dssdev; dssdev->name = pdata->name; diff --git a/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c b/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c new file mode 100644 index 000000000000..84a6b3367124 --- /dev/null +++ b/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c @@ -0,0 +1,285 @@ +/* + * OPA362 analog video amplifier with output/power control + * + * Copyright (C) 2014 Golden Delicious Computers + * Author: H. Nikolaus Schaller <hns@goldelico.com> + * + * based on encoder-tfp410 + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/of_gpio.h> + +#include <video/omapdss.h> + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct gpio_desc *enable_gpio; + + struct omap_video_timings timings; +}; + +#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) + +static int opa362_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(dssdev->dev, "connect\n"); + + if (omapdss_device_is_connected(dssdev)) + return -EBUSY; + + r = in->ops.atv->connect(in, dssdev); + if (r) + return r; + + dst->src = dssdev; + dssdev->dst = dst; + + return 0; +} + +static void opa362_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "disconnect\n"); + + WARN_ON(!omapdss_device_is_connected(dssdev)); + if (!omapdss_device_is_connected(dssdev)) + return; + + WARN_ON(dst != dssdev->dst); + if (dst != dssdev->dst) + return; + + dst->src = NULL; + dssdev->dst = NULL; + + in->ops.atv->disconnect(in, &ddata->dssdev); +} + +static int opa362_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(dssdev->dev, "enable\n"); + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.atv->set_timings(in, &ddata->timings); + + r = in->ops.atv->enable(in); + if (r) + return r; + + if (ddata->enable_gpio) + gpiod_set_value_cansleep(ddata->enable_gpio, 1); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void opa362_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "disable\n"); + + if (!omapdss_device_is_enabled(dssdev)) + return; + + if (ddata->enable_gpio) + gpiod_set_value_cansleep(ddata->enable_gpio, 0); + + in->ops.atv->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void opa362_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "set_timings\n"); + + ddata->timings = *timings; + dssdev->panel.timings = *timings; + + in->ops.atv->set_timings(in, timings); +} + +static void opa362_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(dssdev->dev, "get_timings\n"); + + *timings = ddata->timings; +} + +static int opa362_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "check_timings\n"); + + return in->ops.atv->check_timings(in, timings); +} + +static void opa362_set_type(struct omap_dss_device *dssdev, + enum omap_dss_venc_type type) +{ + /* we can only drive a COMPOSITE output */ + WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE); + +} + +static const struct omapdss_atv_ops opa362_atv_ops = { + .connect = opa362_connect, + .disconnect = opa362_disconnect, + + .enable = opa362_enable, + .disable = opa362_disable, + + .check_timings = opa362_check_timings, + .set_timings = opa362_set_timings, + .get_timings = opa362_get_timings, + + .set_type = opa362_set_type, +}; + +static int opa362_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev, *in; + struct gpio_desc *gpio; + int r; + + dev_dbg(&pdev->dev, "probe\n"); + + if (node == NULL) { + dev_err(&pdev->dev, "Unable to find device tree\n"); + return -EINVAL; + } + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + + gpio = devm_gpiod_get(&pdev->dev, "enable"); + if (IS_ERR(gpio)) { + if (PTR_ERR(gpio) != -ENOENT) + return PTR_ERR(gpio); + + gpio = NULL; + } else { + gpiod_direction_output(gpio, 0); + } + + ddata->enable_gpio = gpio; + + in = omapdss_of_find_source_for_first_ep(node); + if (IS_ERR(in)) { + dev_err(&pdev->dev, "failed to find video source\n"); + return PTR_ERR(in); + } + + ddata->in = in; + + dssdev = &ddata->dssdev; + dssdev->ops.atv = &opa362_atv_ops; + dssdev->dev = &pdev->dev; + dssdev->type = OMAP_DISPLAY_TYPE_VENC; + dssdev->output_type = OMAP_DISPLAY_TYPE_VENC; + dssdev->owner = THIS_MODULE; + + r = omapdss_register_output(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register output\n"); + goto err_reg; + } + + return 0; +err_reg: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit opa362_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_output(&ddata->dssdev); + + WARN_ON(omapdss_device_is_enabled(dssdev)); + if (omapdss_device_is_enabled(dssdev)) + opa362_disable(dssdev); + + WARN_ON(omapdss_device_is_connected(dssdev)); + if (omapdss_device_is_connected(dssdev)) + opa362_disconnect(dssdev, dssdev->dst); + + omap_dss_put_device(in); + + return 0; +} + +static const struct of_device_id opa362_of_match[] = { + { .compatible = "omapdss,ti,opa362", }, + {}, +}; +MODULE_DEVICE_TABLE(of, opa362_of_match); + +static struct platform_driver opa362_driver = { + .probe = opa362_probe, + .remove = __exit_p(opa362_remove), + .driver = { + .name = "amplifier-opa362", + .owner = THIS_MODULE, + .of_match_table = opa362_of_match, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(opa362_driver); + +MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); +MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c b/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c index 7f3e11b16c86..990af6baeb0f 100644 --- a/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c +++ b/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c @@ -29,33 +29,10 @@ struct panel_drv_data { int hpd_gpio; struct omap_video_timings timings; - - struct completion hpd_completion; }; #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) -static irqreturn_t tpd_hpd_irq_handler(int irq, void *data) -{ - struct panel_drv_data *ddata = data; - bool hpd; - - hpd = gpio_get_value_cansleep(ddata->hpd_gpio); - - dev_dbg(ddata->dssdev.dev, "hpd %d\n", hpd); - - if (gpio_is_valid(ddata->ls_oe_gpio)) { - if (hpd) - gpio_set_value_cansleep(ddata->ls_oe_gpio, 1); - else - gpio_set_value_cansleep(ddata->ls_oe_gpio, 0); - } - - complete_all(&ddata->hpd_completion); - - return IRQ_HANDLED; -} - static int tpd_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { @@ -70,23 +47,10 @@ static int tpd_connect(struct omap_dss_device *dssdev, dst->src = dssdev; dssdev->dst = dst; - reinit_completion(&ddata->hpd_completion); - gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); /* DC-DC converter needs at max 300us to get to 90% of 5V */ udelay(300); - /* - * If there's a cable connected, wait for the hpd irq to trigger, - * which turns on the level shifters. - */ - if (gpio_get_value_cansleep(ddata->hpd_gpio)) { - unsigned long to; - to = wait_for_completion_timeout(&ddata->hpd_completion, - msecs_to_jiffies(250)); - WARN_ON_ONCE(to == 0); - } - return 0; } @@ -179,11 +143,20 @@ static int tpd_read_edid(struct omap_dss_device *dssdev, { struct panel_drv_data *ddata = to_panel_data(dssdev); struct omap_dss_device *in = ddata->in; + int r; if (!gpio_get_value_cansleep(ddata->hpd_gpio)) return -ENODEV; - return in->ops.hdmi->read_edid(in, edid, len); + if (gpio_is_valid(ddata->ls_oe_gpio)) + gpio_set_value_cansleep(ddata->ls_oe_gpio, 1); + + r = in->ops.hdmi->read_edid(in, edid, len); + + if (gpio_is_valid(ddata->ls_oe_gpio)) + gpio_set_value_cansleep(ddata->ls_oe_gpio, 0); + + return r; } static bool tpd_detect(struct omap_dss_device *dssdev) @@ -309,8 +282,6 @@ static int tpd_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - init_completion(&ddata->hpd_completion); - if (dev_get_platdata(&pdev->dev)) { r = tpd_probe_pdata(pdev); if (r) @@ -340,13 +311,6 @@ static int tpd_probe(struct platform_device *pdev) if (r) goto err_gpio; - r = devm_request_threaded_irq(&pdev->dev, gpio_to_irq(ddata->hpd_gpio), - NULL, tpd_hpd_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, "hpd", ddata); - if (r) - goto err_irq; - dssdev = &ddata->dssdev; dssdev->ops.hdmi = &tpd_hdmi_ops; dssdev->dev = &pdev->dev; @@ -365,7 +329,6 @@ static int tpd_probe(struct platform_device *pdev) return 0; err_reg: -err_irq: err_gpio: omap_dss_put_device(ddata->in); return r; diff --git a/drivers/video/fbdev/omap2/dss/Makefile b/drivers/video/fbdev/omap2/dss/Makefile index 2ea9d382354c..b5136d3d4b77 100644 --- a/drivers/video/fbdev/omap2/dss/Makefile +++ b/drivers/video/fbdev/omap2/dss/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o obj-$(CONFIG_OMAP2_DSS) += omapdss.o # Core DSS files omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \ - output.o dss-of.o pll.o + output.o dss-of.o pll.o video-pll.o # DSS compat layer files omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \ dispc-compat.o display-sysfs.o diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c index 9850d9ef9a9d..31b743c70272 100644 --- a/drivers/video/fbdev/omap2/dss/dispc.c +++ b/drivers/video/fbdev/omap2/dss/dispc.c @@ -36,6 +36,9 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/sizes.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/of.h> #include <video/omapdss.h> @@ -117,6 +120,9 @@ static struct { const struct dispc_features *feat; bool is_enabled; + + struct regmap *syscon_pol; + u32 syscon_pol_offset; } dispc; enum omap_color_component { @@ -2958,6 +2964,25 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, FLD_VAL(vsync_level, 12, 12); dispc_write_reg(DISPC_POL_FREQ(channel), l); + + if (dispc.syscon_pol) { + const int shifts[] = { + [OMAP_DSS_CHANNEL_LCD] = 0, + [OMAP_DSS_CHANNEL_LCD2] = 1, + [OMAP_DSS_CHANNEL_LCD3] = 2, + }; + + u32 mask, val; + + mask = (1 << 0) | (1 << 3) | (1 << 6); + val = (rf << 0) | (ipc << 3) | (onoff << 6); + + mask <<= 16 + shifts[channel]; + val <<= 16 + shifts[channel]; + + regmap_update_bits(dispc.syscon_pol, dispc.syscon_pol_offset, + mask, val); + } } /* change name to mode? */ @@ -3037,10 +3062,16 @@ unsigned long dispc_fclk_rate(void) break; case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: pll = dss_pll_find("dsi0"); + if (!pll) + pll = dss_pll_find("video0"); + r = pll->cinfo.clkout[0]; break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: pll = dss_pll_find("dsi1"); + if (!pll) + pll = dss_pll_find("video1"); + r = pll->cinfo.clkout[0]; break; default: @@ -3069,10 +3100,16 @@ unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) break; case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: pll = dss_pll_find("dsi0"); + if (!pll) + pll = dss_pll_find("video0"); + r = pll->cinfo.clkout[0]; break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: pll = dss_pll_find("dsi1"); + if (!pll) + pll = dss_pll_find("video1"); + r = pll->cinfo.clkout[0]; break; default: @@ -3668,6 +3705,7 @@ static int __init dispc_init_features(struct platform_device *pdev) break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: src = &omap54xx_dispc_feats; break; @@ -3728,6 +3766,7 @@ static int __init omap_dispchw_probe(struct platform_device *pdev) u32 rev; int r = 0; struct resource *dispc_mem; + struct device_node *np = pdev->dev.of_node; dispc.pdev = pdev; @@ -3754,6 +3793,20 @@ static int __init omap_dispchw_probe(struct platform_device *pdev) return -ENODEV; } + if (np && of_property_read_bool(np, "syscon-pol")) { + dispc.syscon_pol = syscon_regmap_lookup_by_phandle(np, "syscon-pol"); + if (IS_ERR(dispc.syscon_pol)) { + dev_err(&pdev->dev, "failed to get syscon-pol regmap\n"); + return PTR_ERR(dispc.syscon_pol); + } + + if (of_property_read_u32_index(np, "syscon-pol", 1, + &dispc.syscon_pol_offset)) { + dev_err(&pdev->dev, "failed to get syscon-pol offset\n"); + return -EINVAL; + } + } + pm_runtime_enable(&pdev->dev); r = dispc_runtime_get(); @@ -3832,6 +3885,7 @@ static const struct of_device_id dispc_of_match[] = { { .compatible = "ti,omap3-dispc", }, { .compatible = "ti,omap4-dispc", }, { .compatible = "ti,omap5-dispc", }, + { .compatible = "ti,dra7-dispc", }, {}, }; diff --git a/drivers/video/fbdev/omap2/dss/dpi.c b/drivers/video/fbdev/omap2/dss/dpi.c index 9a2f8c3b102d..f83e7b030249 100644 --- a/drivers/video/fbdev/omap2/dss/dpi.c +++ b/drivers/video/fbdev/omap2/dss/dpi.c @@ -106,6 +106,17 @@ static struct dss_pll *dpi_get_pll(enum omap_channel channel) return NULL; } + case OMAPDSS_VER_DRA7xx: + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + case OMAP_DSS_CHANNEL_LCD2: + return dss_pll_find("video0"); + case OMAP_DSS_CHANNEL_LCD3: + return dss_pll_find("video1"); + default: + return NULL; + } + default: return NULL; } @@ -590,6 +601,10 @@ static void dpi_init_pll(struct dpi_data *dpi) if (!pll) return; + /* On DRA7 we need to set a mux to use the PLL */ + if (omapdss_get_version() == OMAPDSS_VER_DRA7xx) + dss_ctrl_pll_set_control_mux(pll->id, dpi->output.dispc_channel); + if (dpi_verify_dsi_pll(pll)) { DSSWARN("DSI PLL not operational\n"); return; @@ -615,6 +630,17 @@ static enum omap_channel dpi_get_channel(int port_num) case OMAPDSS_VER_AM43xx: return OMAP_DSS_CHANNEL_LCD; + case OMAPDSS_VER_DRA7xx: + switch (port_num) { + case 2: + return OMAP_DSS_CHANNEL_LCD3; + case 1: + return OMAP_DSS_CHANNEL_LCD2; + case 0: + default: + return OMAP_DSS_CHANNEL_LCD; + } + case OMAPDSS_VER_OMAP4430_ES1: case OMAPDSS_VER_OMAP4430_ES2: case OMAPDSS_VER_OMAP4: diff --git a/drivers/video/fbdev/omap2/dss/dsi.c b/drivers/video/fbdev/omap2/dss/dsi.c index 3e44c580b1f8..5081f6fb1737 100644 --- a/drivers/video/fbdev/omap2/dss/dsi.c +++ b/drivers/video/fbdev/omap2/dss/dsi.c @@ -5238,6 +5238,7 @@ static int dsi_init_pll_data(struct platform_device *dsidev) } pll->name = dsi->module_id == 0 ? "dsi0" : "dsi1"; + pll->id = dsi->module_id == 0 ? DSS_PLL_DSI1 : DSS_PLL_DSI2; pll->clkin = clk; pll->base = dsi->pll_base; diff --git a/drivers/video/fbdev/omap2/dss/dss.c b/drivers/video/fbdev/omap2/dss/dss.c index 9987154d50b4..a6d10d4279f3 100644 --- a/drivers/video/fbdev/omap2/dss/dss.c +++ b/drivers/video/fbdev/omap2/dss/dss.c @@ -34,7 +34,10 @@ #include <linux/pm_runtime.h> #include <linux/gfp.h> #include <linux/sizes.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include <linux/of.h> +#include <linux/regulator/consumer.h> #include <video/omapdss.h> @@ -63,14 +66,11 @@ struct dss_reg { #define REG_FLD_MOD(idx, val, start, end) \ dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) -static int dss_runtime_get(void); -static void dss_runtime_put(void); - struct dss_features { u8 fck_div_max; u8 dss_fck_multiplier; const char *parent_clk_name; - enum omap_display_type *ports; + const enum omap_display_type *ports; int num_ports; int (*dpi_select_source)(int port, enum omap_channel channel); }; @@ -78,6 +78,8 @@ struct dss_features { static struct { struct platform_device *pdev; void __iomem *base; + struct regmap *syscon_pll_ctrl; + u32 syscon_pll_ctrl_offset; struct clk *parent_clk; struct clk *dss_clk; @@ -95,6 +97,9 @@ static struct { u32 ctx[DSS_SZ_REGS / sizeof(u32)]; const struct dss_features *feat; + + struct dss_pll *video1_pll; + struct dss_pll *video2_pll; } dss; static const char * const dss_generic_clk_source_names[] = { @@ -158,6 +163,99 @@ static void dss_restore_context(void) #undef SR #undef RR +void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable) +{ + unsigned shift; + unsigned val; + + if (!dss.syscon_pll_ctrl) + return; + + val = !enable; + + switch (pll_id) { + case DSS_PLL_VIDEO1: + shift = 0; + break; + case DSS_PLL_VIDEO2: + shift = 1; + break; + case DSS_PLL_HDMI: + shift = 2; + break; + default: + DSSERR("illegal DSS PLL ID %d\n", pll_id); + return; + } + + regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset, + 1 << shift, val << shift); +} + +void dss_ctrl_pll_set_control_mux(enum dss_pll_id pll_id, + enum omap_channel channel) +{ + unsigned shift, val; + + if (!dss.syscon_pll_ctrl) + return; + + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + shift = 3; + + switch (pll_id) { + case DSS_PLL_VIDEO1: + val = 0; break; + case DSS_PLL_HDMI: + val = 1; break; + default: + DSSERR("error in PLL mux config for LCD\n"); + return; + } + + break; + case OMAP_DSS_CHANNEL_LCD2: + shift = 5; + + switch (pll_id) { + case DSS_PLL_VIDEO1: + val = 0; break; + case DSS_PLL_VIDEO2: + val = 1; break; + case DSS_PLL_HDMI: + val = 2; break; + default: + DSSERR("error in PLL mux config for LCD2\n"); + return; + } + + break; + case OMAP_DSS_CHANNEL_LCD3: + shift = 7; + + switch (pll_id) { + case DSS_PLL_VIDEO1: + val = 1; break; + case DSS_PLL_VIDEO2: + val = 0; break; + case DSS_PLL_HDMI: + val = 2; break; + default: + DSSERR("error in PLL mux config for LCD3\n"); + return; + } + + break; + default: + DSSERR("error in PLL mux config\n"); + return; + } + + regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset, + 0x3 << shift, val << shift); +} + void dss_sdi_init(int datapairs) { u32 l; @@ -605,6 +703,26 @@ static int dss_dpi_select_source_omap5(int port, enum omap_channel channel) return 0; } +static int dss_dpi_select_source_dra7xx(int port, enum omap_channel channel) +{ + switch (port) { + case 0: + return dss_dpi_select_source_omap5(port, channel); + case 1: + if (channel != OMAP_DSS_CHANNEL_LCD2) + return -EINVAL; + break; + case 2: + if (channel != OMAP_DSS_CHANNEL_LCD3) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + int dss_dpi_select_source(int port, enum omap_channel channel) { return dss.feat->dpi_select_source(port, channel); @@ -643,7 +761,7 @@ static void dss_put_clocks(void) clk_put(dss.parent_clk); } -static int dss_runtime_get(void) +int dss_runtime_get(void) { int r; @@ -654,7 +772,7 @@ static int dss_runtime_get(void) return r < 0 ? r : 0; } -static void dss_runtime_put(void) +void dss_runtime_put(void) { int r; @@ -677,15 +795,21 @@ void dss_debug_dump_clocks(struct seq_file *s) #endif -static enum omap_display_type omap2plus_ports[] = { +static const enum omap_display_type omap2plus_ports[] = { OMAP_DISPLAY_TYPE_DPI, }; -static enum omap_display_type omap34xx_ports[] = { +static const enum omap_display_type omap34xx_ports[] = { OMAP_DISPLAY_TYPE_DPI, OMAP_DISPLAY_TYPE_SDI, }; +static const enum omap_display_type dra7xx_ports[] = { + OMAP_DISPLAY_TYPE_DPI, + OMAP_DISPLAY_TYPE_DPI, + OMAP_DISPLAY_TYPE_DPI, +}; + static const struct dss_features omap24xx_dss_feats __initconst = { /* * fck div max is really 16, but the divider range has gaps. The range @@ -744,6 +868,15 @@ static const struct dss_features am43xx_dss_feats __initconst = { .num_ports = ARRAY_SIZE(omap2plus_ports), }; +static const struct dss_features dra7xx_dss_feats __initconst = { + .fck_div_max = 64, + .dss_fck_multiplier = 1, + .parent_clk_name = "dpll_per_x2_ck", + .dpi_select_source = &dss_dpi_select_source_dra7xx, + .ports = dra7xx_ports, + .num_ports = ARRAY_SIZE(dra7xx_ports), +}; + static int __init dss_init_features(struct platform_device *pdev) { const struct dss_features *src; @@ -784,6 +917,10 @@ static int __init dss_init_features(struct platform_device *pdev) src = &am43xx_dss_feats; break; + case OMAPDSS_VER_DRA7xx: + src = &dra7xx_dss_feats; + break; + default: return -ENODEV; } @@ -884,8 +1021,10 @@ static void __exit dss_uninit_ports(struct platform_device *pdev) static int __init omap_dsshw_probe(struct platform_device *pdev) { struct resource *dss_mem; + struct device_node *np = pdev->dev.of_node; u32 rev; int r; + struct regulator *pll_regulator; dss.pdev = pdev; @@ -940,6 +1079,57 @@ static int __init omap_dsshw_probe(struct platform_device *pdev) dss_init_ports(pdev); + if (np && of_property_read_bool(np, "syscon-pll-ctrl")) { + dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np, + "syscon-pll-ctrl"); + if (IS_ERR(dss.syscon_pll_ctrl)) { + dev_err(&pdev->dev, + "failed to get syscon-pll-ctrl regmap\n"); + return PTR_ERR(dss.syscon_pll_ctrl); + } + + if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1, + &dss.syscon_pll_ctrl_offset)) { + dev_err(&pdev->dev, + "failed to get syscon-pll-ctrl offset\n"); + return -EINVAL; + } + } + + pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video"); + if (IS_ERR(pll_regulator)) { + r = PTR_ERR(pll_regulator); + + switch (r) { + case -ENOENT: + pll_regulator = NULL; + break; + + case -EPROBE_DEFER: + return -EPROBE_DEFER; + + default: + DSSERR("can't get DPLL VDDA regulator\n"); + return r; + } + } + + if (of_property_match_string(np, "reg-names", "pll1") >= 0) { + dss.video1_pll = dss_video_pll_init(pdev, 0, pll_regulator); + if (IS_ERR(dss.video1_pll)) { + r = PTR_ERR(dss.video1_pll); + goto err_pll_init; + } + } + + if (of_property_match_string(np, "reg-names", "pll2") >= 0) { + dss.video2_pll = dss_video_pll_init(pdev, 1, pll_regulator); + if (IS_ERR(dss.video2_pll)) { + r = PTR_ERR(dss.video2_pll); + goto err_pll_init; + } + } + rev = dss_read_reg(DSS_REVISION); printk(KERN_INFO "OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); @@ -950,6 +1140,12 @@ static int __init omap_dsshw_probe(struct platform_device *pdev) return 0; +err_pll_init: + if (dss.video1_pll) + dss_video_pll_uninit(dss.video1_pll); + + if (dss.video2_pll) + dss_video_pll_uninit(dss.video2_pll); err_runtime_get: pm_runtime_disable(&pdev->dev); err_setup_clocks: @@ -959,6 +1155,12 @@ err_setup_clocks: static int __exit omap_dsshw_remove(struct platform_device *pdev) { + if (dss.video1_pll) + dss_video_pll_uninit(dss.video1_pll); + + if (dss.video2_pll) + dss_video_pll_uninit(dss.video2_pll); + dss_uninit_ports(pdev); pm_runtime_disable(&pdev->dev); @@ -1003,6 +1205,7 @@ static const struct of_device_id dss_of_match[] = { { .compatible = "ti,omap3-dss", }, { .compatible = "ti,omap4-dss", }, { .compatible = "ti,omap5-dss", }, + { .compatible = "ti,dra7-dss", }, {}, }; diff --git a/drivers/video/fbdev/omap2/dss/dss.h b/drivers/video/fbdev/omap2/dss/dss.h index 14fb0c23f4a2..4812eee2622a 100644 --- a/drivers/video/fbdev/omap2/dss/dss.h +++ b/drivers/video/fbdev/omap2/dss/dss.h @@ -100,6 +100,14 @@ enum dss_writeback_channel { DSS_WB_LCD3_MGR = 7, }; +enum dss_pll_id { + DSS_PLL_DSI1, + DSS_PLL_DSI2, + DSS_PLL_HDMI, + DSS_PLL_VIDEO1, + DSS_PLL_VIDEO2, +}; + struct dss_pll; #define DSS_PLL_MAX_HSDIVS 4 @@ -150,6 +158,7 @@ struct dss_pll_hw { struct dss_pll { const char *name; + enum dss_pll_id id; struct clk *clkin; struct regulator *regulator; @@ -250,6 +259,9 @@ void dss_overlay_kobj_uninit(struct omap_overlay *ovl); int dss_init_platform_driver(void) __init; void dss_uninit_platform_driver(void); +int dss_runtime_get(void); +void dss_runtime_put(void); + unsigned long dss_get_dispc_clk_rate(void); int dss_dpi_select_source(int port, enum omap_channel channel); void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); @@ -257,6 +269,11 @@ enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); void dss_dump_clocks(struct seq_file *s); +/* DSS VIDEO PLL */ +struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, + struct regulator *regulator); +void dss_video_pll_uninit(struct dss_pll *pll); + /* dss-of */ struct device_node *dss_of_port_get_parent_device(struct device_node *port); u32 dss_of_port_get_port_number(struct device_node *port); @@ -265,6 +282,10 @@ u32 dss_of_port_get_port_number(struct device_node *port); void dss_debug_dump_clocks(struct seq_file *s); #endif +void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable); +void dss_ctrl_pll_set_control_mux(enum dss_pll_id pll_id, + enum omap_channel channel); + void dss_sdi_init(int datapairs); int dss_sdi_enable(void); void dss_sdi_disable(void); @@ -446,5 +467,6 @@ int dss_pll_write_config_type_a(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo); int dss_pll_write_config_type_b(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo); +int dss_pll_wait_reset_done(struct dss_pll *pll); #endif diff --git a/drivers/video/fbdev/omap2/dss/dss_features.c b/drivers/video/fbdev/omap2/dss/dss_features.c index 0e3da809473c..376270b777f8 100644 --- a/drivers/video/fbdev/omap2/dss/dss_features.c +++ b/drivers/video/fbdev/omap2/dss/dss_features.c @@ -223,7 +223,7 @@ static const enum omap_dss_output_id omap5_dss_supported_outputs[] = { OMAP_DSS_OUTPUT_DSI1 | OMAP_DSS_OUTPUT_DSI2, /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DSS_OUTPUT_HDMI | OMAP_DSS_OUTPUT_DPI, + OMAP_DSS_OUTPUT_HDMI, /* OMAP_DSS_CHANNEL_LCD2 */ OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | @@ -943,6 +943,7 @@ void dss_features_init(enum omapdss_version version) break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: omap_current_dss_features = &omap5_dss_features; break; diff --git a/drivers/video/fbdev/omap2/dss/hdmi5.c b/drivers/video/fbdev/omap2/dss/hdmi5.c index 39aae3aa7136..3f0b34a7031a 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi5.c +++ b/drivers/video/fbdev/omap2/dss/hdmi5.c @@ -787,6 +787,7 @@ static const struct dev_pm_ops hdmi_pm_ops = { static const struct of_device_id hdmi_of_match[] = { { .compatible = "ti,omap5-hdmi", }, + { .compatible = "ti,dra7-hdmi", }, {}, }; diff --git a/drivers/video/fbdev/omap2/dss/hdmi_phy.c b/drivers/video/fbdev/omap2/dss/hdmi_phy.c index bc9e07d2afbe..1f5d19c119ce 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_phy.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_phy.c @@ -208,6 +208,7 @@ static int hdmi_phy_init_features(struct platform_device *pdev) break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: src = &omap54xx_phy_feats; break; diff --git a/drivers/video/fbdev/omap2/dss/hdmi_pll.c b/drivers/video/fbdev/omap2/dss/hdmi_pll.c index ac83ef5cfd7d..06e23a7c432c 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_pll.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_pll.c @@ -104,6 +104,8 @@ static int hdmi_pll_enable(struct dss_pll *dsspll) struct hdmi_wp_data *wp = pll->wp; u16 r = 0; + dss_ctrl_pll_enable(DSS_PLL_HDMI, true); + r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); if (r) return r; @@ -117,6 +119,8 @@ static void hdmi_pll_disable(struct dss_pll *dsspll) struct hdmi_wp_data *wp = pll->wp; hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); + + dss_ctrl_pll_enable(DSS_PLL_HDMI, false); } static const struct dss_pll_ops dsi_pll_ops = { @@ -185,6 +189,7 @@ static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data } pll->name = "hdmi"; + pll->id = DSS_PLL_HDMI; pll->base = hpll->base; pll->clkin = clk; @@ -196,6 +201,7 @@ static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: pll->hw = &dss_omap5_hdmi_pll_hw; break; diff --git a/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c b/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c index 2f0822ee3ff9..42b87f95267c 100644 --- a/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c +++ b/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c @@ -186,6 +186,7 @@ static const struct of_device_id omapdss_of_match[] __initconst = { { .compatible = "ti,omap3-dss", }, { .compatible = "ti,omap4-dss", }, { .compatible = "ti,omap5-dss", }, + { .compatible = "ti,dra7-dss", }, {}, }; diff --git a/drivers/video/fbdev/omap2/dss/pll.c b/drivers/video/fbdev/omap2/dss/pll.c index 335ffac224b9..f974ddcd3b6e 100644 --- a/drivers/video/fbdev/omap2/dss/pll.c +++ b/drivers/video/fbdev/omap2/dss/pll.c @@ -222,6 +222,16 @@ static int wait_for_bit_change(void __iomem *reg, int bitnum, int value) return !value; } +int dss_pll_wait_reset_done(struct dss_pll *pll) +{ + void __iomem *base = pll->base; + + if (wait_for_bit_change(base + PLL_STATUS, 0, 1) != 1) + return -ETIMEDOUT; + else + return 0; +} + static int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask) { int t = 100; diff --git a/drivers/video/fbdev/omap2/dss/video-pll.c b/drivers/video/fbdev/omap2/dss/video-pll.c new file mode 100644 index 000000000000..b1ec59e42940 --- /dev/null +++ b/drivers/video/fbdev/omap2/dss/video-pll.c @@ -0,0 +1,211 @@ +/* +* Copyright (C) 2014 Texas Instruments Ltd +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published by +* the Free Software Foundation. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/sched.h> + +#include <video/omapdss.h> + +#include "dss.h" +#include "dss_features.h" + +struct dss_video_pll { + struct dss_pll pll; + + struct device *dev; + + void __iomem *clkctrl_base; +}; + +#define REG_MOD(reg, val, start, end) \ + writel_relaxed(FLD_MOD(readl_relaxed(reg), val, start, end), reg) + +static void dss_dpll_enable_scp_clk(struct dss_video_pll *vpll) +{ + REG_MOD(vpll->clkctrl_base, 1, 14, 14); /* CIO_CLK_ICG */ +} + +static void dss_dpll_disable_scp_clk(struct dss_video_pll *vpll) +{ + REG_MOD(vpll->clkctrl_base, 0, 14, 14); /* CIO_CLK_ICG */ +} + +static void dss_dpll_power_enable(struct dss_video_pll *vpll) +{ + REG_MOD(vpll->clkctrl_base, 2, 31, 30); /* PLL_POWER_ON_ALL */ + + /* + * DRA7x PLL CTRL's PLL_PWR_STATUS seems to always return 0, + * so we have to use fixed delay here. + */ + msleep(1); +} + +static void dss_dpll_power_disable(struct dss_video_pll *vpll) +{ + REG_MOD(vpll->clkctrl_base, 0, 31, 30); /* PLL_POWER_OFF */ +} + +static int dss_video_pll_enable(struct dss_pll *pll) +{ + struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll); + int r; + + r = dss_runtime_get(); + if (r) + return r; + + dss_ctrl_pll_enable(pll->id, true); + + dss_dpll_enable_scp_clk(vpll); + + r = dss_pll_wait_reset_done(pll); + if (r) + goto err_reset; + + dss_dpll_power_enable(vpll); + + return 0; + +err_reset: + dss_dpll_disable_scp_clk(vpll); + dss_ctrl_pll_enable(pll->id, false); + dss_runtime_put(); + + return r; +} + +static void dss_video_pll_disable(struct dss_pll *pll) +{ + struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll); + + dss_dpll_power_disable(vpll); + + dss_dpll_disable_scp_clk(vpll); + + dss_ctrl_pll_enable(pll->id, false); + + dss_runtime_put(); +} + +static const struct dss_pll_ops dss_pll_ops = { + .enable = dss_video_pll_enable, + .disable = dss_video_pll_disable, + .set_config = dss_pll_write_config_type_a, +}; + +static const struct dss_pll_hw dss_dra7_video_pll_hw = { + .n_max = (1 << 8) - 1, + .m_max = (1 << 12) - 1, + .mX_max = (1 << 5) - 1, + .fint_min = 500000, + .fint_max = 2500000, + .clkdco_max = 1800000000, + + .n_msb = 8, + .n_lsb = 1, + .m_msb = 20, + .m_lsb = 9, + + .mX_msb[0] = 25, + .mX_lsb[0] = 21, + .mX_msb[1] = 30, + .mX_lsb[1] = 26, + + .has_refsel = true, +}; + +struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, + struct regulator *regulator) +{ + const char * const reg_name[] = { "pll1", "pll2" }; + const char * const clkctrl_name[] = { "pll1_clkctrl", "pll2_clkctrl" }; + const char * const clkin_name[] = { "video1_clk", "video2_clk" }; + + struct resource *res; + struct dss_video_pll *vpll; + void __iomem *pll_base, *clkctrl_base; + struct clk *clk; + struct dss_pll *pll; + int r; + + /* PLL CONTROL */ + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, reg_name[id]); + if (!res) { + dev_err(&pdev->dev, + "missing platform resource data for pll%d\n", id); + return ERR_PTR(-ENODEV); + } + + pll_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pll_base)) { + dev_err(&pdev->dev, "failed to ioremap pll%d reg_name\n", id); + return ERR_CAST(pll_base); + } + + /* CLOCK CONTROL */ + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + clkctrl_name[id]); + if (!res) { + dev_err(&pdev->dev, + "missing platform resource data for pll%d\n", id); + return ERR_PTR(-ENODEV); + } + + clkctrl_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(clkctrl_base)) { + dev_err(&pdev->dev, "failed to ioremap pll%d clkctrl\n", id); + return ERR_CAST(clkctrl_base); + } + + /* CLKIN */ + + clk = devm_clk_get(&pdev->dev, clkin_name[id]); + if (IS_ERR(clk)) { + DSSERR("can't get video pll clkin\n"); + return ERR_CAST(clk); + } + + vpll = devm_kzalloc(&pdev->dev, sizeof(*vpll), GFP_KERNEL); + if (!vpll) + return ERR_PTR(-ENOMEM); + + vpll->dev = &pdev->dev; + vpll->clkctrl_base = clkctrl_base; + + pll = &vpll->pll; + + pll->name = id == 0 ? "video0" : "video1"; + pll->id = id == 0 ? DSS_PLL_VIDEO1 : DSS_PLL_VIDEO2; + pll->clkin = clk; + pll->regulator = regulator; + pll->base = pll_base; + pll->hw = &dss_dra7_video_pll_hw; + pll->ops = &dss_pll_ops; + + r = dss_pll_register(pll); + if (r) + return ERR_PTR(r); + + return pll; +} + +void dss_video_pll_uninit(struct dss_pll *pll) +{ + dss_pll_unregister(pll); +} diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c b/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c index 146b6f5428db..9ddfdd63b84c 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c @@ -137,8 +137,11 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) goto undo; } - if (ovl->manager) - ovl->manager->apply(ovl->manager); + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + goto undo; + } if (pi->enabled) { r = ovl->enable(ovl); diff --git a/drivers/video/fbdev/pvr2fb.c b/drivers/video/fbdev/pvr2fb.c index 7c74f58fc101..0e24eb9c219c 100644 --- a/drivers/video/fbdev/pvr2fb.c +++ b/drivers/video/fbdev/pvr2fb.c @@ -686,10 +686,8 @@ static ssize_t pvr2fb_write(struct fb_info *info, const char *buf, if (!pages) return -ENOMEM; - down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, (unsigned long)buf, - nr_pages, WRITE, 0, pages, NULL); - up_read(¤t->mm->mmap_sem); + ret = get_user_pages_unlocked(current, current->mm, (unsigned long)buf, + nr_pages, WRITE, 0, pages); if (ret < nr_pages) { nr_pages = ret; diff --git a/drivers/video/fbdev/savage/savagefb.h b/drivers/video/fbdev/savage/savagefb.h index dcaab9012ca2..8ff4ab1cb69b 100644 --- a/drivers/video/fbdev/savage/savagefb.h +++ b/drivers/video/fbdev/savage/savagefb.h @@ -351,32 +351,26 @@ static inline void VGAwSEQ(u8 index, u8 val, struct savagefb_par *par) static inline void VGAenablePalette(struct savagefb_par *par) { - u8 tmp; - - tmp = vga_in8(0x3da, par); + vga_in8(0x3da, par); vga_out8(0x3c0, 0x00, par); par->paletteEnabled = 1; } static inline void VGAdisablePalette(struct savagefb_par *par) { - u8 tmp; - - tmp = vga_in8(0x3da, par); + vga_in8(0x3da, par); vga_out8(0x3c0, 0x20, par); par->paletteEnabled = 0; } static inline void VGAwATTR(u8 index, u8 value, struct savagefb_par *par) { - u8 tmp; - if (par->paletteEnabled) index &= ~0x20; else index |= 0x20; - tmp = vga_in8(0x3da, par); + vga_in8(0x3da, par); vga_out8(0x3c0, index, par); vga_out8 (0x3c0, value, par); } diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index 92cac803dee3..1085c0432158 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -402,7 +402,7 @@ static int __init simplefb_init(void) if (ret) return ret; - if (IS_ENABLED(CONFIG_OF) && of_chosen) { + if (IS_ENABLED(CONFIG_OF_ADDRESS) && of_chosen) { for_each_child_of_node(of_chosen, np) { if (of_device_is_compatible(np, "simple-framebuffer")) of_platform_device_create(np, NULL, NULL); diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index f4daa59f0a80..f7ed6d9016f7 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -122,23 +122,6 @@ static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) return ret; } -static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) -{ - struct ssd1307fb_array *array; - int ret; - - array = ssd1307fb_alloc_array(1, SSD1307FB_DATA); - if (!array) - return -ENOMEM; - - array->data[0] = data; - - ret = ssd1307fb_write_array(client, array, 1); - kfree(array); - - return ret; -} - static void ssd1307fb_update_display(struct ssd1307fb_par *par) { struct ssd1307fb_array *array; @@ -320,7 +303,10 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par) /* Set initial contrast */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); - ret = ret & ssd1307fb_write_cmd(par->client, 0x7f); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x7f); if (ret < 0) return ret; @@ -336,63 +322,99 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par) /* Set multiplex ratio value */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO); - ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, par->height - 1); if (ret < 0) return ret; /* set display offset value */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET); + if (ret < 0) + return ret; + ret = ssd1307fb_write_cmd(par->client, 0x20); if (ret < 0) return ret; /* Set clock frequency */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ); - ret = ret & ssd1307fb_write_cmd(par->client, 0xf0); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0xf0); if (ret < 0) return ret; /* Set precharge period in number of ticks from the internal clock */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD); - ret = ret & ssd1307fb_write_cmd(par->client, 0x22); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x22); if (ret < 0) return ret; /* Set COM pins configuration */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG); - ret = ret & ssd1307fb_write_cmd(par->client, 0x22); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x22); if (ret < 0) return ret; /* Set VCOMH */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH); - ret = ret & ssd1307fb_write_cmd(par->client, 0x49); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x49); if (ret < 0) return ret; /* Turn on the DC-DC Charge Pump */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP); - ret = ret & ssd1307fb_write_cmd(par->client, 0x14); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x14); if (ret < 0) return ret; /* Switch to horizontal addressing mode */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); - ret = ret & ssd1307fb_write_cmd(par->client, - SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, + SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); if (ret < 0) return ret; ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); - ret = ret & ssd1307fb_write_cmd(par->client, 0x0); - ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x0); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, par->width - 1); if (ret < 0) return ret; ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); - ret = ret & ssd1307fb_write_cmd(par->client, 0x0); - ret = ret & ssd1307fb_write_cmd(par->client, - par->page_offset + (par->height / 8) - 1); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x0); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, + par->page_offset + (par->height / 8) - 1); if (ret < 0) return ret; @@ -460,7 +482,7 @@ static int ssd1307fb_probe(struct i2c_client *client, par->width = 96; if (of_property_read_u32(node, "solomon,height", &par->height)) - par->width = 16; + par->height = 16; if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset)) par->page_offset = 1; diff --git a/drivers/video/fbdev/vt8500lcdfb.c b/drivers/video/fbdev/vt8500lcdfb.c index ffaf29eeaaba..1a1176bf0906 100644 --- a/drivers/video/fbdev/vt8500lcdfb.c +++ b/drivers/video/fbdev/vt8500lcdfb.c @@ -113,10 +113,8 @@ static int vt8500lcd_set_par(struct fb_info *info) } for (i = 0; i < 8; i++) { - if (bpp_values[i] == info->var.bits_per_pixel) { + if (bpp_values[i] == info->var.bits_per_pixel) reg_bpp = i; - continue; - } } control0 = readl(fbi->regbase) & ~0xf; diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 9e758a8f890d..162689227a23 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -27,10 +27,12 @@ #include <linux/export.h> #include <linux/hdmi.h> #include <linux/string.h> +#include <linux/device.h> -static void hdmi_infoframe_checksum(void *buffer, size_t size) +#define hdmi_log(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__) + +static u8 hdmi_infoframe_checksum(u8 *ptr, size_t size) { - u8 *ptr = buffer; u8 csum = 0; size_t i; @@ -38,7 +40,14 @@ static void hdmi_infoframe_checksum(void *buffer, size_t size) for (i = 0; i < size; i++) csum += ptr[i]; - ptr[3] = 256 - csum; + return 256 - csum; +} + +static void hdmi_infoframe_set_checksum(void *buffer, size_t size) +{ + u8 *ptr = buffer; + + ptr[3] = hdmi_infoframe_checksum(buffer, size); } /** @@ -136,7 +145,7 @@ ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, ptr[11] = frame->right_bar & 0xff; ptr[12] = (frame->right_bar >> 8) & 0xff; - hdmi_infoframe_checksum(buffer, length); + hdmi_infoframe_set_checksum(buffer, length); return length; } @@ -206,7 +215,7 @@ ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, ptr[24] = frame->sdi; - hdmi_infoframe_checksum(buffer, length); + hdmi_infoframe_set_checksum(buffer, length); return length; } @@ -281,7 +290,7 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, if (frame->downmix_inhibit) ptr[4] |= BIT(7); - hdmi_infoframe_checksum(buffer, length); + hdmi_infoframe_set_checksum(buffer, length); return length; } @@ -373,7 +382,7 @@ ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, ptr[9] = (frame->s3d_ext_data & 0xf) << 4; } - hdmi_infoframe_checksum(buffer, length); + hdmi_infoframe_set_checksum(buffer, length); return length; } @@ -434,3 +443,802 @@ hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size) return length; } EXPORT_SYMBOL(hdmi_infoframe_pack); + +static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type) +{ + if (type < 0x80 || type > 0x9f) + return "Invalid"; + switch (type) { + case HDMI_INFOFRAME_TYPE_VENDOR: + return "Vendor"; + case HDMI_INFOFRAME_TYPE_AVI: + return "Auxiliary Video Information (AVI)"; + case HDMI_INFOFRAME_TYPE_SPD: + return "Source Product Description (SPD)"; + case HDMI_INFOFRAME_TYPE_AUDIO: + return "Audio"; + } + return "Reserved"; +} + +static void hdmi_infoframe_log_header(const char *level, + struct device *dev, + struct hdmi_any_infoframe *frame) +{ + hdmi_log("HDMI infoframe: %s, version %u, length %u\n", + hdmi_infoframe_type_get_name(frame->type), + frame->version, frame->length); +} + +static const char *hdmi_colorspace_get_name(enum hdmi_colorspace colorspace) +{ + switch (colorspace) { + case HDMI_COLORSPACE_RGB: + return "RGB"; + case HDMI_COLORSPACE_YUV422: + return "YCbCr 4:2:2"; + case HDMI_COLORSPACE_YUV444: + return "YCbCr 4:4:4"; + case HDMI_COLORSPACE_YUV420: + return "YCbCr 4:2:0"; + case HDMI_COLORSPACE_RESERVED4: + return "Reserved (4)"; + case HDMI_COLORSPACE_RESERVED5: + return "Reserved (5)"; + case HDMI_COLORSPACE_RESERVED6: + return "Reserved (6)"; + case HDMI_COLORSPACE_IDO_DEFINED: + return "IDO Defined"; + } + return "Invalid"; +} + +static const char *hdmi_scan_mode_get_name(enum hdmi_scan_mode scan_mode) +{ + switch (scan_mode) { + case HDMI_SCAN_MODE_NONE: + return "No Data"; + case HDMI_SCAN_MODE_OVERSCAN: + return "Overscan"; + case HDMI_SCAN_MODE_UNDERSCAN: + return "Underscan"; + case HDMI_SCAN_MODE_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char *hdmi_colorimetry_get_name(enum hdmi_colorimetry colorimetry) +{ + switch (colorimetry) { + case HDMI_COLORIMETRY_NONE: + return "No Data"; + case HDMI_COLORIMETRY_ITU_601: + return "ITU601"; + case HDMI_COLORIMETRY_ITU_709: + return "ITU709"; + case HDMI_COLORIMETRY_EXTENDED: + return "Extended"; + } + return "Invalid"; +} + +static const char * +hdmi_picture_aspect_get_name(enum hdmi_picture_aspect picture_aspect) +{ + switch (picture_aspect) { + case HDMI_PICTURE_ASPECT_NONE: + return "No Data"; + case HDMI_PICTURE_ASPECT_4_3: + return "4:3"; + case HDMI_PICTURE_ASPECT_16_9: + return "16:9"; + case HDMI_PICTURE_ASPECT_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char * +hdmi_active_aspect_get_name(enum hdmi_active_aspect active_aspect) +{ + if (active_aspect < 0 || active_aspect > 0xf) + return "Invalid"; + + switch (active_aspect) { + case HDMI_ACTIVE_ASPECT_16_9_TOP: + return "16:9 Top"; + case HDMI_ACTIVE_ASPECT_14_9_TOP: + return "14:9 Top"; + case HDMI_ACTIVE_ASPECT_16_9_CENTER: + return "16:9 Center"; + case HDMI_ACTIVE_ASPECT_PICTURE: + return "Same as Picture"; + case HDMI_ACTIVE_ASPECT_4_3: + return "4:3"; + case HDMI_ACTIVE_ASPECT_16_9: + return "16:9"; + case HDMI_ACTIVE_ASPECT_14_9: + return "14:9"; + case HDMI_ACTIVE_ASPECT_4_3_SP_14_9: + return "4:3 SP 14:9"; + case HDMI_ACTIVE_ASPECT_16_9_SP_14_9: + return "16:9 SP 14:9"; + case HDMI_ACTIVE_ASPECT_16_9_SP_4_3: + return "16:9 SP 4:3"; + } + return "Reserved"; +} + +static const char * +hdmi_extended_colorimetry_get_name(enum hdmi_extended_colorimetry ext_col) +{ + switch (ext_col) { + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_601: + return "xvYCC 601"; + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_709: + return "xvYCC 709"; + case HDMI_EXTENDED_COLORIMETRY_S_YCC_601: + return "sYCC 601"; + case HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601: + return "Adobe YCC 601"; + case HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB: + return "Adobe RGB"; + case HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM: + return "BT.2020 Constant Luminance"; + case HDMI_EXTENDED_COLORIMETRY_BT2020: + return "BT.2020"; + case HDMI_EXTENDED_COLORIMETRY_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char * +hdmi_quantization_range_get_name(enum hdmi_quantization_range qrange) +{ + switch (qrange) { + case HDMI_QUANTIZATION_RANGE_DEFAULT: + return "Default"; + case HDMI_QUANTIZATION_RANGE_LIMITED: + return "Limited"; + case HDMI_QUANTIZATION_RANGE_FULL: + return "Full"; + case HDMI_QUANTIZATION_RANGE_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char *hdmi_nups_get_name(enum hdmi_nups nups) +{ + switch (nups) { + case HDMI_NUPS_UNKNOWN: + return "Unknown Non-uniform Scaling"; + case HDMI_NUPS_HORIZONTAL: + return "Horizontally Scaled"; + case HDMI_NUPS_VERTICAL: + return "Vertically Scaled"; + case HDMI_NUPS_BOTH: + return "Horizontally and Vertically Scaled"; + } + return "Invalid"; +} + +static const char * +hdmi_ycc_quantization_range_get_name(enum hdmi_ycc_quantization_range qrange) +{ + switch (qrange) { + case HDMI_YCC_QUANTIZATION_RANGE_LIMITED: + return "Limited"; + case HDMI_YCC_QUANTIZATION_RANGE_FULL: + return "Full"; + } + return "Invalid"; +} + +static const char * +hdmi_content_type_get_name(enum hdmi_content_type content_type) +{ + switch (content_type) { + case HDMI_CONTENT_TYPE_GRAPHICS: + return "Graphics"; + case HDMI_CONTENT_TYPE_PHOTO: + return "Photo"; + case HDMI_CONTENT_TYPE_CINEMA: + return "Cinema"; + case HDMI_CONTENT_TYPE_GAME: + return "Game"; + } + return "Invalid"; +} + +/** + * hdmi_avi_infoframe_log() - log info of HDMI AVI infoframe + * @level: logging level + * @dev: device + * @frame: HDMI AVI infoframe + */ +static void hdmi_avi_infoframe_log(const char *level, + struct device *dev, + struct hdmi_avi_infoframe *frame) +{ + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + + hdmi_log(" colorspace: %s\n", + hdmi_colorspace_get_name(frame->colorspace)); + hdmi_log(" scan mode: %s\n", + hdmi_scan_mode_get_name(frame->scan_mode)); + hdmi_log(" colorimetry: %s\n", + hdmi_colorimetry_get_name(frame->colorimetry)); + hdmi_log(" picture aspect: %s\n", + hdmi_picture_aspect_get_name(frame->picture_aspect)); + hdmi_log(" active aspect: %s\n", + hdmi_active_aspect_get_name(frame->active_aspect)); + hdmi_log(" itc: %s\n", frame->itc ? "IT Content" : "No Data"); + hdmi_log(" extended colorimetry: %s\n", + hdmi_extended_colorimetry_get_name(frame->extended_colorimetry)); + hdmi_log(" quantization range: %s\n", + hdmi_quantization_range_get_name(frame->quantization_range)); + hdmi_log(" nups: %s\n", hdmi_nups_get_name(frame->nups)); + hdmi_log(" video code: %u\n", frame->video_code); + hdmi_log(" ycc quantization range: %s\n", + hdmi_ycc_quantization_range_get_name(frame->ycc_quantization_range)); + hdmi_log(" hdmi content type: %s\n", + hdmi_content_type_get_name(frame->content_type)); + hdmi_log(" pixel repeat: %u\n", frame->pixel_repeat); + hdmi_log(" bar top %u, bottom %u, left %u, right %u\n", + frame->top_bar, frame->bottom_bar, + frame->left_bar, frame->right_bar); +} + +static const char *hdmi_spd_sdi_get_name(enum hdmi_spd_sdi sdi) +{ + if (sdi < 0 || sdi > 0xff) + return "Invalid"; + switch (sdi) { + case HDMI_SPD_SDI_UNKNOWN: + return "Unknown"; + case HDMI_SPD_SDI_DSTB: + return "Digital STB"; + case HDMI_SPD_SDI_DVDP: + return "DVD Player"; + case HDMI_SPD_SDI_DVHS: + return "D-VHS"; + case HDMI_SPD_SDI_HDDVR: + return "HDD Videorecorder"; + case HDMI_SPD_SDI_DVC: + return "DVC"; + case HDMI_SPD_SDI_DSC: + return "DSC"; + case HDMI_SPD_SDI_VCD: + return "Video CD"; + case HDMI_SPD_SDI_GAME: + return "Game"; + case HDMI_SPD_SDI_PC: + return "PC General"; + case HDMI_SPD_SDI_BD: + return "Blu-Ray Disc (BD)"; + case HDMI_SPD_SDI_SACD: + return "Super Audio CD"; + case HDMI_SPD_SDI_HDDVD: + return "HD DVD"; + case HDMI_SPD_SDI_PMP: + return "PMP"; + } + return "Reserved"; +} + +/** + * hdmi_spd_infoframe_log() - log info of HDMI SPD infoframe + * @level: logging level + * @dev: device + * @frame: HDMI SPD infoframe + */ +static void hdmi_spd_infoframe_log(const char *level, + struct device *dev, + struct hdmi_spd_infoframe *frame) +{ + u8 buf[17]; + + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + + memset(buf, 0, sizeof(buf)); + + strncpy(buf, frame->vendor, 8); + hdmi_log(" vendor: %s\n", buf); + strncpy(buf, frame->product, 16); + hdmi_log(" product: %s\n", buf); + hdmi_log(" source device information: %s (0x%x)\n", + hdmi_spd_sdi_get_name(frame->sdi), frame->sdi); +} + +static const char * +hdmi_audio_coding_type_get_name(enum hdmi_audio_coding_type coding_type) +{ + switch (coding_type) { + case HDMI_AUDIO_CODING_TYPE_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_CODING_TYPE_PCM: + return "PCM"; + case HDMI_AUDIO_CODING_TYPE_AC3: + return "AC-3"; + case HDMI_AUDIO_CODING_TYPE_MPEG1: + return "MPEG1"; + case HDMI_AUDIO_CODING_TYPE_MP3: + return "MP3"; + case HDMI_AUDIO_CODING_TYPE_MPEG2: + return "MPEG2"; + case HDMI_AUDIO_CODING_TYPE_AAC_LC: + return "AAC"; + case HDMI_AUDIO_CODING_TYPE_DTS: + return "DTS"; + case HDMI_AUDIO_CODING_TYPE_ATRAC: + return "ATRAC"; + case HDMI_AUDIO_CODING_TYPE_DSD: + return "One Bit Audio"; + case HDMI_AUDIO_CODING_TYPE_EAC3: + return "Dolby Digital +"; + case HDMI_AUDIO_CODING_TYPE_DTS_HD: + return "DTS-HD"; + case HDMI_AUDIO_CODING_TYPE_MLP: + return "MAT (MLP)"; + case HDMI_AUDIO_CODING_TYPE_DST: + return "DST"; + case HDMI_AUDIO_CODING_TYPE_WMA_PRO: + return "WMA PRO"; + case HDMI_AUDIO_CODING_TYPE_CXT: + return "Refer to CXT"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_sample_size_get_name(enum hdmi_audio_sample_size sample_size) +{ + switch (sample_size) { + case HDMI_AUDIO_SAMPLE_SIZE_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_SAMPLE_SIZE_16: + return "16 bit"; + case HDMI_AUDIO_SAMPLE_SIZE_20: + return "20 bit"; + case HDMI_AUDIO_SAMPLE_SIZE_24: + return "24 bit"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_sample_frequency_get_name(enum hdmi_audio_sample_frequency freq) +{ + switch (freq) { + case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: + return "32 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: + return "44.1 kHz (CD)"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: + return "48 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: + return "88.2 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: + return "96 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_176400: + return "176.4 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_192000: + return "192 kHz"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_coding_type_ext_get_name(enum hdmi_audio_coding_type_ext ctx) +{ + if (ctx < 0 || ctx > 0x1f) + return "Invalid"; + + switch (ctx) { + case HDMI_AUDIO_CODING_TYPE_EXT_CT: + return "Refer to CT"; + case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC: + return "HE AAC"; + case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2: + return "HE AAC v2"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND: + return "MPEG SURROUND"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC: + return "MPEG-4 HE AAC"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2: + return "MPEG-4 HE AAC v2"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC: + return "MPEG-4 AAC LC"; + case HDMI_AUDIO_CODING_TYPE_EXT_DRA: + return "DRA"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND: + return "MPEG-4 HE AAC + MPEG Surround"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND: + return "MPEG-4 AAC LC + MPEG Surround"; + } + return "Reserved"; +} + +/** + * hdmi_audio_infoframe_log() - log info of HDMI AUDIO infoframe + * @level: logging level + * @dev: device + * @frame: HDMI AUDIO infoframe + */ +static void hdmi_audio_infoframe_log(const char *level, + struct device *dev, + struct hdmi_audio_infoframe *frame) +{ + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + + if (frame->channels) + hdmi_log(" channels: %u\n", frame->channels - 1); + else + hdmi_log(" channels: Refer to stream header\n"); + hdmi_log(" coding type: %s\n", + hdmi_audio_coding_type_get_name(frame->coding_type)); + hdmi_log(" sample size: %s\n", + hdmi_audio_sample_size_get_name(frame->sample_size)); + hdmi_log(" sample frequency: %s\n", + hdmi_audio_sample_frequency_get_name(frame->sample_frequency)); + hdmi_log(" coding type ext: %s\n", + hdmi_audio_coding_type_ext_get_name(frame->coding_type_ext)); + hdmi_log(" channel allocation: 0x%x\n", + frame->channel_allocation); + hdmi_log(" level shift value: %u dB\n", + frame->level_shift_value); + hdmi_log(" downmix inhibit: %s\n", + frame->downmix_inhibit ? "Yes" : "No"); +} + +static const char * +hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct) +{ + if (s3d_struct < 0 || s3d_struct > 0xf) + return "Invalid"; + + switch (s3d_struct) { + case HDMI_3D_STRUCTURE_FRAME_PACKING: + return "Frame Packing"; + case HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE: + return "Field Alternative"; + case HDMI_3D_STRUCTURE_LINE_ALTERNATIVE: + return "Line Alternative"; + case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL: + return "Side-by-side (Full)"; + case HDMI_3D_STRUCTURE_L_DEPTH: + return "L + Depth"; + case HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH: + return "L + Depth + Graphics + Graphics-depth"; + case HDMI_3D_STRUCTURE_TOP_AND_BOTTOM: + return "Top-and-Bottom"; + case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF: + return "Side-by-side (Half)"; + default: + break; + } + return "Reserved"; +} + +/** + * hdmi_vendor_infoframe_log() - log info of HDMI VENDOR infoframe + * @level: logging level + * @dev: device + * @frame: HDMI VENDOR infoframe + */ +static void +hdmi_vendor_any_infoframe_log(const char *level, + struct device *dev, + union hdmi_vendor_any_infoframe *frame) +{ + struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + + if (frame->any.oui != HDMI_IEEE_OUI) { + hdmi_log(" not a HDMI vendor infoframe\n"); + return; + } + if (hvf->vic == 0 && hvf->s3d_struct == HDMI_3D_STRUCTURE_INVALID) { + hdmi_log(" empty frame\n"); + return; + } + + if (hvf->vic) + hdmi_log(" HDMI VIC: %u\n", hvf->vic); + if (hvf->s3d_struct != HDMI_3D_STRUCTURE_INVALID) { + hdmi_log(" 3D structure: %s\n", + hdmi_3d_structure_get_name(hvf->s3d_struct)); + if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + hdmi_log(" 3D extension data: %d\n", + hvf->s3d_ext_data); + } +} + +/** + * hdmi_infoframe_log() - log info of HDMI infoframe + * @level: logging level + * @dev: device + * @frame: HDMI infoframe + */ +void hdmi_infoframe_log(const char *level, + struct device *dev, + union hdmi_infoframe *frame) +{ + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + hdmi_avi_infoframe_log(level, dev, &frame->avi); + break; + case HDMI_INFOFRAME_TYPE_SPD: + hdmi_spd_infoframe_log(level, dev, &frame->spd); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + hdmi_audio_infoframe_log(level, dev, &frame->audio); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + hdmi_vendor_any_infoframe_log(level, dev, &frame->vendor); + break; + } +} +EXPORT_SYMBOL(hdmi_infoframe_log); + +/** + * hdmi_avi_infoframe_unpack() - unpack binary buffer to a HDMI AVI infoframe + * @buffer: source buffer + * @frame: HDMI AVI infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Auxiliary Video (AVI) information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, + void *buffer) +{ + u8 *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_AVI || + ptr[1] != 2 || + ptr[2] != HDMI_AVI_INFOFRAME_SIZE) + return -EINVAL; + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AVI)) != 0) + return -EINVAL; + + ret = hdmi_avi_infoframe_init(frame); + if (ret) + return ret; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + frame->colorspace = (ptr[0] >> 5) & 0x3; + if (ptr[0] & 0x10) + frame->active_aspect = ptr[1] & 0xf; + if (ptr[0] & 0x8) { + frame->top_bar = (ptr[5] << 8) + ptr[6]; + frame->bottom_bar = (ptr[7] << 8) + ptr[8]; + } + if (ptr[0] & 0x4) { + frame->left_bar = (ptr[9] << 8) + ptr[10]; + frame->right_bar = (ptr[11] << 8) + ptr[12]; + } + frame->scan_mode = ptr[0] & 0x3; + + frame->colorimetry = (ptr[1] >> 6) & 0x3; + frame->picture_aspect = (ptr[1] >> 4) & 0x3; + frame->active_aspect = ptr[1] & 0xf; + + frame->itc = ptr[2] & 0x80 ? true : false; + frame->extended_colorimetry = (ptr[2] >> 4) & 0x7; + frame->quantization_range = (ptr[2] >> 2) & 0x3; + frame->nups = ptr[2] & 0x3; + + frame->video_code = ptr[3] & 0x7f; + frame->ycc_quantization_range = (ptr[4] >> 6) & 0x3; + frame->content_type = (ptr[4] >> 4) & 0x3; + + frame->pixel_repeat = ptr[4] & 0xf; + + return 0; +} + +/** + * hdmi_spd_infoframe_unpack() - unpack binary buffer to a HDMI SPD infoframe + * @buffer: source buffer + * @frame: HDMI SPD infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Source Product Description (SPD) information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, + void *buffer) +{ + u8 *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_SPD || + ptr[1] != 1 || + ptr[2] != HDMI_SPD_INFOFRAME_SIZE) { + return -EINVAL; + } + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(SPD)) != 0) + return -EINVAL; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ret = hdmi_spd_infoframe_init(frame, ptr, ptr + 8); + if (ret) + return ret; + + frame->sdi = ptr[24]; + + return 0; +} + +/** + * hdmi_audio_infoframe_unpack() - unpack binary buffer to a HDMI AUDIO infoframe + * @buffer: source buffer + * @frame: HDMI Audio infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Audio information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, + void *buffer) +{ + u8 *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_AUDIO || + ptr[1] != 1 || + ptr[2] != HDMI_AUDIO_INFOFRAME_SIZE) { + return -EINVAL; + } + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AUDIO)) != 0) + return -EINVAL; + + ret = hdmi_audio_infoframe_init(frame); + if (ret) + return ret; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + frame->channels = ptr[0] & 0x7; + frame->coding_type = (ptr[0] >> 4) & 0xf; + frame->sample_size = ptr[1] & 0x3; + frame->sample_frequency = (ptr[1] >> 2) & 0x7; + frame->coding_type_ext = ptr[2] & 0x1f; + frame->channel_allocation = ptr[3]; + frame->level_shift_value = (ptr[4] >> 3) & 0xf; + frame->downmix_inhibit = ptr[4] & 0x80 ? true : false; + + return 0; +} + +/** + * hdmi_vendor_infoframe_unpack() - unpack binary buffer to a HDMI vendor infoframe + * @buffer: source buffer + * @frame: HDMI Vendor infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Vendor information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int +hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, + void *buffer) +{ + u8 *ptr = buffer; + size_t length; + int ret; + u8 hdmi_video_format; + struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR || + ptr[1] != 1 || + (ptr[2] != 5 && ptr[2] != 6)) + return -EINVAL; + + length = ptr[2]; + + if (hdmi_infoframe_checksum(buffer, + HDMI_INFOFRAME_HEADER_SIZE + length) != 0) + return -EINVAL; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + /* HDMI OUI */ + if ((ptr[0] != 0x03) || + (ptr[1] != 0x0c) || + (ptr[2] != 0x00)) + return -EINVAL; + + hdmi_video_format = ptr[3] >> 5; + + if (hdmi_video_format > 0x2) + return -EINVAL; + + ret = hdmi_vendor_infoframe_init(hvf); + if (ret) + return ret; + + hvf->length = length; + + if (hdmi_video_format == 0x1) { + hvf->vic = ptr[4]; + } else if (hdmi_video_format == 0x2) { + hvf->s3d_struct = ptr[4] >> 4; + if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) { + if (length == 6) + hvf->s3d_ext_data = ptr[5] >> 4; + else + return -EINVAL; + } + } + + return 0; +} + +/** + * hdmi_infoframe_unpack() - unpack binary buffer to a HDMI infoframe + * @buffer: source buffer + * @frame: HDMI infoframe + * + * Unpacks the information contained in binary buffer @buffer into a structured + * @frame of a HDMI infoframe. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer) +{ + int ret; + u8 *ptr = buffer; + + switch (ptr[0]) { + case HDMI_INFOFRAME_TYPE_AVI: + ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer); + break; + case HDMI_INFOFRAME_TYPE_SPD: + ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(hdmi_infoframe_unpack); diff --git a/drivers/video/vgastate.c b/drivers/video/vgastate.c index b91c466225b9..548c751d2415 100644 --- a/drivers/video/vgastate.c +++ b/drivers/video/vgastate.c @@ -2,7 +2,7 @@ * linux/drivers/video/vgastate.c -- VGA state save/restore * * Copyright 2002 James Simmons - * + * * Copyright history from vga16fb.c: * Copyright 1999 Ben Pfaff and Petr Vandrovec * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm @@ -10,7 +10,7 @@ * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this - * archive for more details. + * archive for more details. * */ #include <linux/module.h> @@ -29,16 +29,16 @@ struct regstate { __u8 *gfx; __u8 *seq; __u8 misc; -}; +}; -static inline unsigned char vga_rcrtcs(void __iomem *regbase, unsigned short iobase, +static inline unsigned char vga_rcrtcs(void __iomem *regbase, unsigned short iobase, unsigned char reg) { vga_w(regbase, iobase + 0x4, reg); return vga_r(regbase, iobase + 0x5); } -static inline void vga_wcrtcs(void __iomem *regbase, unsigned short iobase, +static inline void vga_wcrtcs(void __iomem *regbase, unsigned short iobase, unsigned char reg, unsigned char val) { vga_w(regbase, iobase + 0x4, reg); @@ -71,7 +71,7 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) gr6 = vga_rgfx(state->vgabase, VGA_GFX_MISC); seq2 = vga_rseq(state->vgabase, VGA_SEQ_PLANE_WRITE); seq4 = vga_rseq(state->vgabase, VGA_SEQ_MEMORY_MODE); - + /* blank screen */ seq1 = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE); vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1); @@ -85,7 +85,7 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x2); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 4 * 8192; i++) + for (i = 0; i < 4 * 8192; i++) saved->vga_font0[i] = vga_r(fbbase, i); } @@ -96,10 +96,10 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x3); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < state->memsize; i++) + for (i = 0; i < state->memsize; i++) saved->vga_font1[i] = vga_r(fbbase, i); } - + /* save font at plane 0/1 */ if (state->flags & VGA_SAVE_TEXT) { vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x1); @@ -107,7 +107,7 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 8192; i++) + for (i = 0; i < 8192; i++) saved->vga_text[i] = vga_r(fbbase, i); vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x2); @@ -115,8 +115,8 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x1); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 8192; i++) - saved->vga_text[8192+i] = vga_r(fbbase + 2 * 8192, i); + for (i = 0; i < 8192; i++) + saved->vga_text[8192+i] = vga_r(fbbase + 2 * 8192, i); } /* restore regs */ @@ -151,7 +151,7 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) gr8 = vga_rgfx(state->vgabase, VGA_GFX_BIT_MASK); seq2 = vga_rseq(state->vgabase, VGA_SEQ_PLANE_WRITE); seq4 = vga_rseq(state->vgabase, VGA_SEQ_MEMORY_MODE); - + /* blank screen */ seq1 = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE); vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1); @@ -163,7 +163,7 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_BIT_MASK, 0xff); vga_wgfx(state->vgabase, VGA_GFX_SR_ENABLE, 0x00); } - + /* restore font at plane 2 */ if (state->flags & VGA_SAVE_FONT0) { vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x4); @@ -171,7 +171,7 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x2); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 4 * 8192; i++) + for (i = 0; i < 4 * 8192; i++) vga_w(fbbase, i, saved->vga_font0[i]); } @@ -182,10 +182,10 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x3); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < state->memsize; i++) + for (i = 0; i < state->memsize; i++) vga_w(fbbase, i, saved->vga_font1[i]); } - + /* restore font at plane 0/1 */ if (state->flags & VGA_SAVE_TEXT) { vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x1); @@ -193,16 +193,16 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 8192; i++) + for (i = 0; i < 8192; i++) vga_w(fbbase, i, saved->vga_text[i]); - + vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x2); vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6); vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x1); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 8192; i++) - vga_w(fbbase, i, saved->vga_text[8192+i]); + for (i = 0; i < 8192; i++) + vga_w(fbbase, i, saved->vga_text[8192+i]); } /* unblank screen */ @@ -222,7 +222,7 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, seq2); vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, seq4); } - + static void save_vga_mode(struct vgastate *state) { struct regstate *saved = (struct regstate *) state->vidstate; @@ -235,10 +235,10 @@ static void save_vga_mode(struct vgastate *state) else iobase = 0x3b0; - for (i = 0; i < state->num_crtc; i++) + for (i = 0; i < state->num_crtc; i++) saved->crtc[i] = vga_rcrtcs(state->vgabase, iobase, i); - - vga_r(state->vgabase, iobase + 0xa); + + vga_r(state->vgabase, iobase + 0xa); vga_w(state->vgabase, VGA_ATT_W, 0x00); for (i = 0; i < state->num_attr; i++) { vga_r(state->vgabase, iobase + 0xa); @@ -247,10 +247,10 @@ static void save_vga_mode(struct vgastate *state) vga_r(state->vgabase, iobase + 0xa); vga_w(state->vgabase, VGA_ATT_W, 0x20); - for (i = 0; i < state->num_gfx; i++) + for (i = 0; i < state->num_gfx; i++) saved->gfx[i] = vga_rgfx(state->vgabase, i); - for (i = 0; i < state->num_seq; i++) + for (i = 0; i < state->num_seq; i++) saved->seq[i] = vga_rseq(state->vgabase, i); } @@ -268,26 +268,26 @@ static void restore_vga_mode(struct vgastate *state) iobase = 0x3b0; /* turn off display */ - vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, + vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, saved->seq[VGA_SEQ_CLOCK_MODE] | 0x20); /* disable sequencer */ vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x01); - + /* enable palette addressing */ vga_r(state->vgabase, iobase + 0xa); vga_w(state->vgabase, VGA_ATT_W, 0x00); - for (i = 2; i < state->num_seq; i++) + for (i = 2; i < state->num_seq; i++) vga_wseq(state->vgabase, i, saved->seq[i]); /* unprotect vga regs */ vga_wcrtcs(state->vgabase, iobase, 17, saved->crtc[17] & ~0x80); - for (i = 0; i < state->num_crtc; i++) + for (i = 0; i < state->num_crtc; i++) vga_wcrtcs(state->vgabase, iobase, i, saved->crtc[i]); - - for (i = 0; i < state->num_gfx; i++) + + for (i = 0; i < state->num_gfx; i++) vga_wgfx(state->vgabase, i, saved->gfx[i]); for (i = 0; i < state->num_attr; i++) { @@ -298,7 +298,7 @@ static void restore_vga_mode(struct vgastate *state) /* reenable sequencer */ vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03); /* turn display on */ - vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, + vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, saved->seq[VGA_SEQ_CLOCK_MODE] & ~(1 << 5)); /* disable video/palette source */ @@ -312,7 +312,7 @@ static void save_vga_cmap(struct vgastate *state) int i; vga_w(state->vgabase, VGA_PEL_MSK, 0xff); - + /* assumes DAC is readable and writable */ vga_w(state->vgabase, VGA_PEL_IR, 0x00); for (i = 0; i < 768; i++) @@ -346,7 +346,7 @@ static void vga_cleanup(struct vgastate *state) state->vidstate = NULL; } } - + int save_vga(struct vgastate *state) { struct regstate *saved; @@ -357,7 +357,7 @@ int save_vga(struct vgastate *state) return 1; state->vidstate = (void *)saved; - + if (state->flags & VGA_SAVE_CMAP) { saved->vga_cmap = vmalloc(768); if (!saved->vga_cmap) { @@ -403,7 +403,7 @@ int save_vga(struct vgastate *state) } if (!state->memsize) state->memsize = 8 * 8192; - + if (!state->membase) state->membase = 0xA0000; @@ -414,7 +414,7 @@ int save_vga(struct vgastate *state) return 1; } - /* + /* * save only first 32K used by vgacon */ if (state->flags & VGA_SAVE_FONT0) { @@ -425,7 +425,7 @@ int save_vga(struct vgastate *state) return 1; } } - /* + /* * largely unused, but if required by the caller * we'll just save everything. */ @@ -448,7 +448,7 @@ int save_vga(struct vgastate *state) return 1; } } - + save_vga_text(state, fbbase); iounmap(fbbase); } |