diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-06-01 16:57:51 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-06-01 16:57:51 -0700 |
commit | 804ce9866d56130032c9c8afc90a1297b7deed56 (patch) | |
tree | 6dd70984f411d2a3624d3f8db7facc3d6396b9ad /drivers | |
parent | f5e7e844a571124ffc117d4696787d6afc4fc5ae (diff) | |
parent | c895305e806b4346006d3cfba2b432d52268ecd3 (diff) |
Merge tag 'fbdev-updates-for-3.5' of git://github.com/schandinat/linux-2.6
Pull fbdev updates from Florian Tobias Schandinat:
- driver for AUO-K1900 and AUO-K1901 epaper controller
- large updates for OMAP (e.g. decouple HDMI audio and video)
- some updates for Exynos and SH Mobile
- various other small fixes and cleanups
* tag 'fbdev-updates-for-3.5' of git://github.com/schandinat/linux-2.6: (130 commits)
video: bfin_adv7393fb: Fix cleanup code
video: exynos_dp: reduce delay time when configuring video setting
video: exynos_dp: move sw reset prioir to enabling sw defined function
video: exynos_dp: use devm_ functions
fb: handle NULL pointers in framebuffer release
OMAPDSS: HDMI: OMAP4: Update IRQ flags for the HPD IRQ request
OMAPDSS: Apply VENC timings even if panel is disabled
OMAPDSS: VENC/DISPC: Delay dividing Y resolution for managers connected to VENC
OMAPDSS: DISPC: Support rotation through TILER
OMAPDSS: VRFB: remove compiler warnings when CONFIG_BUG=n
OMAPFB: remove compiler warnings when CONFIG_BUG=n
OMAPDSS: remove compiler warnings when CONFIG_BUG=n
OMAPDSS: DISPC: fix usage of dispc_ovl_set_accu_uv
OMAPDSS: use DSI_FIFO_BUG workaround only for manual update displays
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
OMAPDSS: DISPC: Update Accumulator configuration for chroma plane
drivers/video: fsl-diu-fb: don't initialize the THRESHOLDS registers
video: exynos mipi dsi: support reverse panel type
video: exynos mipi dsi: Properly interpret the interrupt source flags
video: exynos mipi dsi: Avoid races in probe()
...
Diffstat (limited to 'drivers')
66 files changed, 4734 insertions, 1841 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a290be51a1f4..0217f7415ef5 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2210,7 +2210,7 @@ config FB_XILINX config FB_COBALT tristate "Cobalt server LCD frame buffer support" - depends on FB && MIPS_COBALT + depends on FB && (MIPS_COBALT || MIPS_SEAD3) config FB_SH7760 bool "SH7760/SH7763/SH7720/SH7721 LCDC support" @@ -2382,6 +2382,39 @@ config FB_BROADSHEET and could also have been called by other names when coupled with a bridge adapter. +config FB_AUO_K190X + tristate "AUO-K190X EPD controller support" + depends on FB + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + help + Provides support for epaper controllers from the K190X series + of AUO. These controllers can be used to drive epaper displays + from Sipix. + + This option enables the common support, shared by the individual + controller drivers. You will also have to enable the driver + for the controller type used in your device. + +config FB_AUO_K1900 + tristate "AUO-K1900 EPD controller support" + depends on FB && FB_AUO_K190X + help + This driver implements support for the AUO K1900 epd-controller. + This controller can drive Sipix epaper displays but can only do + serial updates, reducing the number of possible frames per second. + +config FB_AUO_K1901 + tristate "AUO-K1901 EPD controller support" + depends on FB && FB_AUO_K190X + help + This driver implements support for the AUO K1901 epd-controller. + This controller can drive Sipix epaper displays and supports + concurrent updates, making higher frames per second possible. + config FB_JZ4740 tristate "JZ4740 LCD framebuffer support" depends on FB && MACH_JZ4740 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9356add945b3..ee8dafb69e36 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -118,6 +118,9 @@ obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o obj-$(CONFIG_FB_MAXINE) += maxinefb.o obj-$(CONFIG_FB_METRONOME) += metronomefb.o obj-$(CONFIG_FB_BROADSHEET) += broadsheetfb.o +obj-$(CONFIG_FB_AUO_K190X) += auo_k190x.o +obj-$(CONFIG_FB_AUO_K1900) += auo_k1900fb.o +obj-$(CONFIG_FB_AUO_K1901) += auo_k1901fb.o obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o obj-$(CONFIG_FB_SH7760) += sh7760fb.o obj-$(CONFIG_FB_IMX) += imxfb.o diff --git a/drivers/video/auo_k1900fb.c b/drivers/video/auo_k1900fb.c new file mode 100644 index 000000000000..c36cf961dcb2 --- /dev/null +++ b/drivers/video/auo_k1900fb.c @@ -0,0 +1,198 @@ +/* + * auok190xfb.c -- FB driver for AUO-K1900 controllers + * + * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de> + * + * based on broadsheetfb.c + * + * Copyright (C) 2008, Jaya Kumar + * + * 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. + * + * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. + * + * This driver is written to be used with the AUO-K1900 display controller. + * + * It is intended to be architecture independent. A board specific driver + * must be used to perform all the physical IO interactions. + * + * The controller supports different update modes: + * mode0+1 16 step gray (4bit) + * mode2 4 step gray (2bit) - FIXME: add strange refresh + * mode3 2 step gray (1bit) - FIXME: add strange refresh + * mode4 handwriting mode (strange behaviour) + * mode5 automatic selection of update mode + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +/* + * AUO-K1900 specific commands + */ + +#define AUOK1900_CMD_PARTIALDISP 0x1001 +#define AUOK1900_CMD_ROTATION 0x1006 +#define AUOK1900_CMD_LUT_STOP 0x1009 + +#define AUOK1900_INIT_TEMP_AVERAGE (1 << 13) +#define AUOK1900_INIT_ROTATE(_x) ((_x & 0x3) << 10) +#define AUOK1900_INIT_RESOLUTION(_res) ((_res & 0x7) << 2) + +static void auok1900_init(struct auok190xfb_par *par) +{ + struct auok190x_board *board = par->board; + u16 init_param = 0; + + init_param |= AUOK1900_INIT_TEMP_AVERAGE; + init_param |= AUOK1900_INIT_ROTATE(par->rotation); + init_param |= AUOK190X_INIT_INVERSE_WHITE; + init_param |= AUOK190X_INIT_FORMAT0; + init_param |= AUOK1900_INIT_RESOLUTION(par->resolution); + init_param |= AUOK190X_INIT_SHIFT_RIGHT; + + auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param); + + /* let the controller finish */ + board->wait_for_rdy(par); +} + +static void auok1900_update_region(struct auok190xfb_par *par, int mode, + u16 y1, u16 y2) +{ + struct device *dev = par->info->device; + unsigned char *buf = (unsigned char *)par->info->screen_base; + int xres = par->info->var.xres; + u16 args[4]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + /* y1 and y2 must be a multiple of 2 so drop the lowest bit */ + y1 &= 0xfffe; + y2 &= 0xfffe; + + dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n", + 1, y1+1, xres, y2-y1, mode); + + /* to FIX handle different partial update modes */ + args[0] = mode | 1; + args[1] = y1 + 1; + args[2] = xres; + args[3] = y2 - y1; + buf += y1 * xres; + auok190x_send_cmdargs_pixels(par, AUOK1900_CMD_PARTIALDISP, 4, args, + ((y2 - y1) * xres)/2, (u16 *) buf); + auok190x_send_command(par, AUOK190X_CMD_DATA_STOP); + + par->update_cnt++; + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static void auok1900fb_dpy_update_pages(struct auok190xfb_par *par, + u16 y1, u16 y2) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(1); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1900_update_region(par, mode, y1, y2); +} + +static void auok1900fb_dpy_update(struct auok190xfb_par *par) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(0); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1900_update_region(par, mode, 0, par->info->var.yres); + par->update_cnt = 0; +} + +static bool auok1900fb_need_refresh(struct auok190xfb_par *par) +{ + return (par->update_cnt > 10); +} + +static int __devinit auok1900fb_probe(struct platform_device *pdev) +{ + struct auok190x_init_data init; + struct auok190x_board *board; + + /* pick up board specific routines */ + board = pdev->dev.platform_data; + if (!board) + return -EINVAL; + + /* fill temporary init struct for common init */ + init.id = "auo_k1900fb"; + init.board = board; + init.update_partial = auok1900fb_dpy_update_pages; + init.update_all = auok1900fb_dpy_update; + init.need_refresh = auok1900fb_need_refresh; + init.init = auok1900_init; + + return auok190x_common_probe(pdev, &init); +} + +static int __devexit auok1900fb_remove(struct platform_device *pdev) +{ + return auok190x_common_remove(pdev); +} + +static struct platform_driver auok1900fb_driver = { + .probe = auok1900fb_probe, + .remove = __devexit_p(auok1900fb_remove), + .driver = { + .owner = THIS_MODULE, + .name = "auo_k1900fb", + .pm = &auok190x_pm, + }, +}; +module_platform_driver(auok1900fb_driver); + +MODULE_DESCRIPTION("framebuffer driver for the AUO-K1900 EPD controller"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k1901fb.c b/drivers/video/auo_k1901fb.c new file mode 100644 index 000000000000..1c054c18616e --- /dev/null +++ b/drivers/video/auo_k1901fb.c @@ -0,0 +1,251 @@ +/* + * auok190xfb.c -- FB driver for AUO-K1901 controllers + * + * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de> + * + * based on broadsheetfb.c + * + * Copyright (C) 2008, Jaya Kumar + * + * 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. + * + * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. + * + * This driver is written to be used with the AUO-K1901 display controller. + * + * It is intended to be architecture independent. A board specific driver + * must be used to perform all the physical IO interactions. + * + * The controller supports different update modes: + * mode0+1 16 step gray (4bit) + * mode2+3 4 step gray (2bit) + * mode4+5 2 step gray (1bit) + * - mode4 is described as "without LUT" + * mode7 automatic selection of update mode + * + * The most interesting difference to the K1900 is the ability to do screen + * updates in an asynchronous fashion. Where the K1900 needs to wait for the + * current update to complete, the K1901 can process later updates already. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +/* + * AUO-K1901 specific commands + */ + +#define AUOK1901_CMD_LUT_INTERFACE 0x0005 +#define AUOK1901_CMD_DMA_START 0x1001 +#define AUOK1901_CMD_CURSOR_START 0x1007 +#define AUOK1901_CMD_CURSOR_STOP AUOK190X_CMD_DATA_STOP +#define AUOK1901_CMD_DDMA_START 0x1009 + +#define AUOK1901_INIT_GATE_PULSE_LOW (0 << 14) +#define AUOK1901_INIT_GATE_PULSE_HIGH (1 << 14) +#define AUOK1901_INIT_SINGLE_GATE (0 << 13) +#define AUOK1901_INIT_DOUBLE_GATE (1 << 13) + +/* Bits to pixels + * Mode 15-12 11-8 7-4 3-0 + * format2 2 T 1 T + * format3 1 T 2 T + * format4 T 2 T 1 + * format5 T 1 T 2 + * + * halftone modes: + * format6 2 2 1 1 + * format7 1 1 2 2 + */ +#define AUOK1901_INIT_FORMAT2 (1 << 7) +#define AUOK1901_INIT_FORMAT3 ((1 << 7) | (1 << 6)) +#define AUOK1901_INIT_FORMAT4 (1 << 8) +#define AUOK1901_INIT_FORMAT5 ((1 << 8) | (1 << 6)) +#define AUOK1901_INIT_FORMAT6 ((1 << 8) | (1 << 7)) +#define AUOK1901_INIT_FORMAT7 ((1 << 8) | (1 << 7) | (1 << 6)) + +/* res[4] to bit 10 + * res[3-0] to bits 5-2 + */ +#define AUOK1901_INIT_RESOLUTION(_res) (((_res & (1 << 4)) << 6) \ + | ((_res & 0xf) << 2)) + +/* + * portrait / landscape orientation in AUOK1901_CMD_DMA_START + */ +#define AUOK1901_DMA_ROTATE90(_rot) ((_rot & 1) << 13) + +/* + * equivalent to 1 << 11, needs the ~ to have same rotation like K1900 + */ +#define AUOK1901_DDMA_ROTATE180(_rot) ((~_rot & 2) << 10) + +static void auok1901_init(struct auok190xfb_par *par) +{ + struct auok190x_board *board = par->board; + u16 init_param = 0; + + init_param |= AUOK190X_INIT_INVERSE_WHITE; + init_param |= AUOK190X_INIT_FORMAT0; + init_param |= AUOK1901_INIT_RESOLUTION(par->resolution); + init_param |= AUOK190X_INIT_SHIFT_LEFT; + + auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param); + + /* let the controller finish */ + board->wait_for_rdy(par); +} + +static void auok1901_update_region(struct auok190xfb_par *par, int mode, + u16 y1, u16 y2) +{ + struct device *dev = par->info->device; + unsigned char *buf = (unsigned char *)par->info->screen_base; + int xres = par->info->var.xres; + u16 args[5]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + /* y1 and y2 must be a multiple of 2 so drop the lowest bit */ + y1 &= 0xfffe; + y2 &= 0xfffe; + + dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n", + 1, y1+1, xres, y2-y1, mode); + + /* K1901: first transfer the region data */ + args[0] = AUOK1901_DMA_ROTATE90(par->rotation) | 1; + args[1] = y1 + 1; + args[2] = xres; + args[3] = y2 - y1; + buf += y1 * xres; + auok190x_send_cmdargs_pixels_nowait(par, AUOK1901_CMD_DMA_START, 4, + args, ((y2 - y1) * xres)/2, + (u16 *) buf); + auok190x_send_command_nowait(par, AUOK190X_CMD_DATA_STOP); + + /* K1901: second tell the controller to update the region with mode */ + args[0] = mode | AUOK1901_DDMA_ROTATE180(par->rotation); + args[1] = 1; + args[2] = y1 + 1; + args[3] = xres; + args[4] = y2 - y1; + auok190x_send_cmdargs_nowait(par, AUOK1901_CMD_DDMA_START, 5, args); + + par->update_cnt++; + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static void auok1901fb_dpy_update_pages(struct auok190xfb_par *par, + u16 y1, u16 y2) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(1); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1901_update_region(par, mode, y1, y2); +} + +static void auok1901fb_dpy_update(struct auok190xfb_par *par) +{ + int mode; + + /* When doing full updates, wait for the controller to be ready + * This will hopefully catch some hangs of the K1901 + */ + par->board->wait_for_rdy(par); + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(0); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1901_update_region(par, mode, 0, par->info->var.yres); + par->update_cnt = 0; +} + +static bool auok1901fb_need_refresh(struct auok190xfb_par *par) +{ + return (par->update_cnt > 10); +} + +static int __devinit auok1901fb_probe(struct platform_device *pdev) +{ + struct auok190x_init_data init; + struct auok190x_board *board; + + /* pick up board specific routines */ + board = pdev->dev.platform_data; + if (!board) + return -EINVAL; + + /* fill temporary init struct for common init */ + init.id = "auo_k1901fb"; + init.board = board; + init.update_partial = auok1901fb_dpy_update_pages; + init.update_all = auok1901fb_dpy_update; + init.need_refresh = auok1901fb_need_refresh; + init.init = auok1901_init; + + return auok190x_common_probe(pdev, &init); +} + +static int __devexit auok1901fb_remove(struct platform_device *pdev) +{ + return auok190x_common_remove(pdev); +} + +static struct platform_driver auok1901fb_driver = { + .probe = auok1901fb_probe, + .remove = __devexit_p(auok1901fb_remove), + .driver = { + .owner = THIS_MODULE, + .name = "auo_k1901fb", + .pm = &auok190x_pm, + }, +}; +module_platform_driver(auok1901fb_driver); + +MODULE_DESCRIPTION("framebuffer driver for the AUO-K1901 EPD controller"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k190x.c b/drivers/video/auo_k190x.c new file mode 100644 index 000000000000..77da6a2f43dc --- /dev/null +++ b/drivers/video/auo_k190x.c @@ -0,0 +1,1046 @@ +/* + * Common code for AUO-K190X framebuffer drivers + * + * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/regulator/consumer.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +struct panel_info { + int w; + int h; +}; + +/* table of panel specific parameters to be indexed into by the board drivers */ +static struct panel_info panel_table[] = { + /* standard 6" */ + [AUOK190X_RESOLUTION_800_600] = { + .w = 800, + .h = 600, + }, + /* standard 9" */ + [AUOK190X_RESOLUTION_1024_768] = { + .w = 1024, + .h = 768, + }, +}; + +/* + * private I80 interface to the board driver + */ + +static void auok190x_issue_data(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_WR, 0); + par->board->set_hdb(par, data); + par->board->set_ctl(par, AUOK190X_I80_WR, 1); +} + +static void auok190x_issue_cmd(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_DC, 0); + auok190x_issue_data(par, data); + par->board->set_ctl(par, AUOK190X_I80_DC, 1); +} + +static int auok190x_issue_pixels(struct auok190xfb_par *par, int size, + u16 *data) +{ + struct device *dev = par->info->device; + int i; + u16 tmp; + + if (size & 3) { + dev_err(dev, "issue_pixels: size %d must be a multiple of 4\n", + size); + return -EINVAL; + } + + for (i = 0; i < (size >> 1); i++) { + par->board->set_ctl(par, AUOK190X_I80_WR, 0); + + /* simple reduction of 8bit staticgray to 4bit gray + * combines 4 * 4bit pixel values into a 16bit value + */ + tmp = (data[2*i] & 0xF0) >> 4; + tmp |= (data[2*i] & 0xF000) >> 8; + tmp |= (data[2*i+1] & 0xF0) << 4; + tmp |= (data[2*i+1] & 0xF000); + + par->board->set_hdb(par, tmp); + par->board->set_ctl(par, AUOK190X_I80_WR, 1); + } + + return 0; +} + +static u16 auok190x_read_data(struct auok190xfb_par *par) +{ + u16 data; + + par->board->set_ctl(par, AUOK190X_I80_OE, 0); + data = par->board->get_hdb(par); + par->board->set_ctl(par, AUOK190X_I80_OE, 1); + + return data; +} + +/* + * Command interface for the controller drivers + */ + +void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, data); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_command_nowait); + +void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int i; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + auok190x_issue_data(par, argv[i]); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_nowait); + +int auok190x_send_command(struct auok190xfb_par *par, u16 data) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_command_nowait(par, data); + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_command); + +int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_cmdargs_nowait(par, cmd, argc, argv); + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs); + +int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int i, ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + argv[i] = auok190x_read_data(par); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_read_cmdargs); + +void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, u16 *data) +{ + int i; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + auok190x_issue_data(par, argv[i]); + + auok190x_issue_pixels(par, size, data); + + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels_nowait); + +int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, u16 *data) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_cmdargs_pixels_nowait(par, cmd, argc, argv, size, data); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels); + +/* + * fbdefio callbacks - common on both controllers. + */ + +static void auok190xfb_dpy_first_io(struct fb_info *info) +{ + /* tell runtime-pm that we wish to use the device in a short time */ + pm_runtime_get(info->device); +} + +/* this is called back from the deferred io workqueue */ +static void auok190xfb_dpy_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + struct fb_deferred_io *fbdefio = info->fbdefio; + struct auok190xfb_par *par = info->par; + u16 yres = info->var.yres; + u16 xres = info->var.xres; + u16 y1 = 0, h = 0; + int prev_index = -1; + struct page *cur; + int h_inc; + int threshold; + + if (!list_empty(pagelist)) + /* the device resume should've been requested through first_io, + * if the resume did not finish until now, wait for it. + */ + pm_runtime_barrier(info->device); + else + /* We reached this via the fsync or some other way. + * In either case the first_io function did not run, + * so we runtime_resume the device here synchronously. + */ + pm_runtime_get_sync(info->device); + + /* Do a full screen update every n updates to prevent + * excessive darkening of the Sipix display. + * If we do this, there is no need to walk the pages. + */ + if (par->need_refresh(par)) { + par->update_all(par); + goto out; + } + + /* height increment is fixed per page */ + h_inc = DIV_ROUND_UP(PAGE_SIZE , xres); + + /* calculate number of pages from pixel height */ + threshold = par->consecutive_threshold / h_inc; + if (threshold < 1) + threshold = 1; + + /* walk the written page list and swizzle the data */ + list_for_each_entry(cur, &fbdefio->pagelist, lru) { + if (prev_index < 0) { + /* just starting so assign first page */ + y1 = (cur->index << PAGE_SHIFT) / xres; + h = h_inc; + } else if ((cur->index - prev_index) <= threshold) { + /* page is within our threshold for single updates */ + h += h_inc * (cur->index - prev_index); + } else { + /* page not consecutive, issue previous update first */ + par->update_partial(par, y1, y1 + h); + + /* start over with our non consecutive page */ + y1 = (cur->index << PAGE_SHIFT) / xres; + h = h_inc; + } + prev_index = cur->index; + } + + /* if we still have any pages to update we do so now */ + if (h >= yres) + /* its a full screen update, just do it */ + par->update_all(par); + else + par->update_partial(par, y1, min((u16) (y1 + h), yres)); + +out: + pm_runtime_mark_last_busy(info->device); + pm_runtime_put_autosuspend(info->device); +} + +/* + * framebuffer operations + */ + +/* + * this is the slow path from userspace. they can seek and write to + * the fb. it's inefficient to do anything less than a full screen draw + */ +static ssize_t auok190xfb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct auok190xfb_par *par = info->par; + unsigned long p = *ppos; + void *dst; + int err = 0; + unsigned long total_size; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + total_size = info->fix.smem_len; + + if (p > total_size) + return -EFBIG; + + if (count > total_size) { + err = -EFBIG; + count = total_size; + } + + if (count + p > total_size) { + if (!err) + err = -ENOSPC; + + count = total_size - p; + } + + dst = (void *)(info->screen_base + p); + + if (copy_from_user(dst, buf, count)) + err = -EFAULT; + + if (!err) + *ppos += count; + + par->update_all(par); + + return (err) ? err : count; +} + +static void auok190xfb_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct auok190xfb_par *par = info->par; + + sys_fillrect(info, rect); + + par->update_all(par); +} + +static void auok190xfb_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct auok190xfb_par *par = info->par; + + sys_copyarea(info, area); + + par->update_all(par); +} + +static void auok190xfb_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct auok190xfb_par *par = info->par; + + sys_imageblit(info, image); + + par->update_all(par); +} + +static int auok190xfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + if (info->var.xres != var->xres || info->var.yres != var->yres || + info->var.xres_virtual != var->xres_virtual || + info->var.yres_virtual != var->yres_virtual) { + pr_info("%s: Resolution not supported: X%u x Y%u\n", + __func__, var->xres, var->yres); + return -EINVAL; + } + + /* + * Memory limit + */ + + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { + pr_info("%s: Memory Limit requested yres_virtual = %u\n", + __func__, var->yres_virtual); + return -ENOMEM; + } + + return 0; +} + +static struct fb_ops auok190xfb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = auok190xfb_write, + .fb_fillrect = auok190xfb_fillrect, + .fb_copyarea = auok190xfb_copyarea, + .fb_imageblit = auok190xfb_imageblit, + .fb_check_var = auok190xfb_check_var, +}; + +/* + * Controller-functions common to both K1900 and K1901 + */ + +static int auok190x_read_temperature(struct auok190xfb_par *par) +{ + struct device *dev = par->info->device; + u16 data[4]; + int temp; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + /* sanitize and split of half-degrees for now */ + temp = ((data[0] & AUOK190X_VERSION_TEMP_MASK) >> 1); + + /* handle positive and negative temperatures */ + if (temp >= 201) + return (255 - temp + 1) * (-1); + else + return temp; +} + +static void auok190x_identify(struct auok190xfb_par *par) +{ + struct device *dev = par->info->device; + u16 data[4]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); + + mutex_unlock(&(par->io_lock)); + + par->epd_type = data[1] & AUOK190X_VERSION_TEMP_MASK; + + par->panel_size_int = AUOK190X_VERSION_SIZE_INT(data[2]); + par->panel_size_float = AUOK190X_VERSION_SIZE_FLOAT(data[2]); + par->panel_model = AUOK190X_VERSION_MODEL(data[2]); + + par->tcon_version = AUOK190X_VERSION_TCON(data[3]); + par->lut_version = AUOK190X_VERSION_LUT(data[3]); + + dev_dbg(dev, "panel %d.%din, model 0x%x, EPD 0x%x TCON-rev 0x%x, LUT-rev 0x%x", + par->panel_size_int, par->panel_size_float, par->panel_model, + par->epd_type, par->tcon_version, par->lut_version); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +/* + * Sysfs functions + */ + +static ssize_t update_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + + return sprintf(buf, "%d\n", par->update_mode); +} + +static ssize_t update_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int mode, ret; + + ret = kstrtoint(buf, 10, &mode); + if (ret) + return ret; + + par->update_mode = mode; + + /* if we enter a better mode, do a full update */ + if (par->last_mode > 1 && mode < par->last_mode) + par->update_all(par); + + return count; +} + +static ssize_t flash_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + + return sprintf(buf, "%d\n", par->flash); +} + +static ssize_t flash_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int flash, ret; + + ret = kstrtoint(buf, 10, &flash); + if (ret) + return ret; + + if (flash > 0) + par->flash = 1; + else + par->flash = 0; + + return count; +} + +static ssize_t temp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int temp; + + temp = auok190x_read_temperature(par); + return sprintf(buf, "%d\n", temp); +} + +static DEVICE_ATTR(update_mode, 0644, update_mode_show, update_mode_store); +static DEVICE_ATTR(flash, 0644, flash_show, flash_store); +static DEVICE_ATTR(temp, 0644, temp_show, NULL); + +static struct attribute *auok190x_attributes[] = { + &dev_attr_update_mode.attr, + &dev_attr_flash.attr, + &dev_attr_temp.attr, + NULL +}; + +static const struct attribute_group auok190x_attr_group = { + .attrs = auok190x_attributes, +}; + +static int auok190x_power(struct auok190xfb_par *par, bool on) +{ + struct auok190x_board *board = par->board; + int ret; + + if (on) { + /* We should maintain POWER up for at least 80ms before set + * RST_N and SLP_N to high (TCON spec 20100803_v35 p59) + */ + ret = regulator_enable(par->regulator); + if (ret) + return ret; + + msleep(200); + gpio_set_value(board->gpio_nrst, 1); + gpio_set_value(board->gpio_nsleep, 1); + msleep(200); + } else { + regulator_disable(par->regulator); + gpio_set_value(board->gpio_nrst, 0); + gpio_set_value(board->gpio_nsleep, 0); + } + + return 0; +} + +/* + * Recovery - powercycle the controller + */ + +static void auok190x_recover(struct auok190xfb_par *par) +{ + auok190x_power(par, 0); + msleep(100); + auok190x_power(par, 1); + + par->init(par); + + /* wait for init to complete */ + par->board->wait_for_rdy(par); +} + +/* + * Power-management + */ + +#ifdef CONFIG_PM +static int auok190x_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + u16 standby_param; + + /* take and keep the lock until we are resumed, as the controller + * will never reach the non-busy state when in standby mode + */ + mutex_lock(&(par->io_lock)); + + if (par->standby) { + dev_warn(dev, "already in standby, runtime-pm pairing mismatch\n"); + mutex_unlock(&(par->io_lock)); + return 0; + } + + /* according to runtime_pm.txt runtime_suspend only means, that the + * device will not process data and will not communicate with the CPU + * As we hold the lock, this stays true even without standby + */ + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "runtime suspend without standby\n"); + goto finish; + } else if (board->quirks & AUOK190X_QUIRK_STANDBYPARAM) { + /* for some TCON versions STANDBY expects a parameter (0) but + * it seems the real tcon version has to be determined yet. + */ + dev_dbg(dev, "runtime suspend with additional empty param\n"); + standby_param = 0; + auok190x_send_cmdargs(par, AUOK190X_CMD_STANDBY, 1, + &standby_param); + } else { + dev_dbg(dev, "runtime suspend without param\n"); + auok190x_send_command(par, AUOK190X_CMD_STANDBY); + } + + msleep(64); + +finish: + par->standby = 1; + + return 0; +} + +static int auok190x_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + if (!par->standby) { + dev_warn(dev, "not in standby, runtime-pm pairing mismatch\n"); + return 0; + } + + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "runtime resume without standby\n"); + } else { + /* when in standby, controller is always busy + * and only accepts the wakeup command + */ + dev_dbg(dev, "runtime resume from standby\n"); + auok190x_send_command_nowait(par, AUOK190X_CMD_WAKEUP); + + msleep(160); + + /* wait for the controller to be ready and release the lock */ + board->wait_for_rdy(par); + } + + par->standby = 0; + + mutex_unlock(&(par->io_lock)); + + return 0; +} + +static int auok190x_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + int ret; + + dev_dbg(dev, "suspend\n"); + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + /* suspend via powering off the ic */ + dev_dbg(dev, "suspend with broken standby\n"); + + auok190x_power(par, 0); + } else { + dev_dbg(dev, "suspend using sleep\n"); + + /* the sleep state can only be entered from the standby state. + * pm_runtime_get_noresume gets called before the suspend call. + * So the devices usage count is >0 but it is not necessarily + * active. + */ + if (!pm_runtime_status_suspended(dev)) { + ret = auok190x_runtime_suspend(dev); + if (ret < 0) { + dev_err(dev, "auok190x_runtime_suspend failed with %d\n", + ret); + return ret; + } + par->manual_standby = 1; + } + + gpio_direction_output(board->gpio_nsleep, 0); + } + + msleep(100); + + return 0; +} + +static int auok190x_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + dev_dbg(dev, "resume\n"); + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "resume with broken standby\n"); + + auok190x_power(par, 1); + + par->init(par); + } else { + dev_dbg(dev, "resume from sleep\n"); + + /* device should be in runtime suspend when we were suspended + * and pm_runtime_put_sync gets called after this function. + * So there is no need to touch the standby mode here at all. + */ + gpio_direction_output(board->gpio_nsleep, 1); + msleep(100); + + /* an additional init call seems to be necessary after sleep */ + auok190x_runtime_resume(dev); + par->init(par); + + /* if we were runtime-suspended before, suspend again*/ + if (!par->manual_standby) + auok190x_runtime_suspend(dev); + else + par->manual_standby = 0; + } + + return 0; +} +#endif + +const struct dev_pm_ops auok190x_pm = { + SET_RUNTIME_PM_OPS(auok190x_runtime_suspend, auok190x_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(auok190x_suspend, auok190x_resume) +}; +EXPORT_SYMBOL_GPL(auok190x_pm); + +/* + * Common probe and remove code + */ + +int __devinit auok190x_common_probe(struct platform_device *pdev, + struct auok190x_init_data *init) +{ + struct auok190x_board *board = init->board; + struct auok190xfb_par *par; + struct fb_info *info; + struct panel_info *panel; + int videomemorysize, ret; + unsigned char *videomemory; + + /* check board contents */ + if (!board->init || !board->cleanup || !board->wait_for_rdy + || !board->set_ctl || !board->set_hdb || !board->get_hdb + || !board->setup_irq) + return -EINVAL; + + info = framebuffer_alloc(sizeof(struct auok190xfb_par), &pdev->dev); + if (!info) + return -ENOMEM; + + par = info->par; + par->info = info; + par->board = board; + par->recover = auok190x_recover; + par->update_partial = init->update_partial; + par->update_all = init->update_all; + par->need_refresh = init->need_refresh; + par->init = init->init; + + /* init update modes */ + par->update_cnt = 0; + par->update_mode = -1; + par->last_mode = -1; + par->flash = 0; + + par->regulator = regulator_get(info->device, "vdd"); + if (IS_ERR(par->regulator)) { + ret = PTR_ERR(par->regulator); + dev_err(info->device, "Failed to get regulator: %d\n", ret); + goto err_reg; + } + + ret = board->init(par); + if (ret) { + dev_err(info->device, "board init failed, %d\n", ret); + goto err_board; + } + + ret = gpio_request(board->gpio_nsleep, "AUOK190x sleep"); + if (ret) { + dev_err(info->device, "could not request sleep gpio, %d\n", + ret); + goto err_gpio1; + } + + ret = gpio_direction_output(board->gpio_nsleep, 0); + if (ret) { + dev_err(info->device, "could not set sleep gpio, %d\n", ret); + goto err_gpio2; + } + + ret = gpio_request(board->gpio_nrst, "AUOK190x reset"); + if (ret) { + dev_err(info->device, "could not request reset gpio, %d\n", + ret); + goto err_gpio2; + } + + ret = gpio_direction_output(board->gpio_nrst, 0); + if (ret) { + dev_err(info->device, "could not set reset gpio, %d\n", ret); + goto err_gpio3; + } + + ret = auok190x_power(par, 1); + if (ret) { + dev_err(info->device, "could not power on the device, %d\n", + ret); + goto err_gpio3; + } + + mutex_init(&par->io_lock); + + init_waitqueue_head(&par->waitq); + + ret = par->board->setup_irq(par->info); + if (ret) { + dev_err(info->device, "could not setup ready-irq, %d\n", ret); + goto err_irq; + } + + /* wait for init to complete */ + par->board->wait_for_rdy(par); + + /* + * From here on the controller can talk to us + */ + + /* initialise fix, var, resolution and rotation */ + + strlcpy(info->fix.id, init->id, 16); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; + info->fix.xpanstep = 0; + info->fix.ypanstep = 0; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + + info->var.bits_per_pixel = 8; + info->var.grayscale = 1; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + + panel = &panel_table[board->resolution]; + + /* if 90 degree rotation, switch width and height */ + if (board->rotation & 1) { + info->var.xres = panel->h; + info->var.yres = panel->w; + info->var.xres_virtual = panel->h; + info->var.yres_virtual = panel->w; + info->fix.line_length = panel->h; + } else { + info->var.xres = panel->w; + info->var.yres = panel->h; + info->var.xres_virtual = panel->w; + info->var.yres_virtual = panel->h; + info->fix.line_length = panel->w; + } + + par->resolution = board->resolution; + par->rotation = board->rotation; + + /* videomemory handling */ + + videomemorysize = roundup((panel->w * panel->h), PAGE_SIZE); + videomemory = vmalloc(videomemorysize); + if (!videomemory) { + ret = -ENOMEM; + goto err_irq; + } + + memset(videomemory, 0, videomemorysize); + info->screen_base = (char *)videomemory; + info->fix.smem_len = videomemorysize; + + info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; + info->fbops = &auok190xfb_ops; + + /* deferred io init */ + + info->fbdefio = devm_kzalloc(info->device, + sizeof(struct fb_deferred_io), + GFP_KERNEL); + if (!info->fbdefio) { + dev_err(info->device, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_defio; + } + + dev_dbg(info->device, "targetting %d frames per second\n", board->fps); + info->fbdefio->delay = HZ / board->fps; + info->fbdefio->first_io = auok190xfb_dpy_first_io, + info->fbdefio->deferred_io = auok190xfb_dpy_deferred_io, + fb_deferred_io_init(info); + + /* color map */ + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) { + dev_err(info->device, "Failed to allocate colormap\n"); + goto err_cmap; + } + + /* controller init */ + + par->consecutive_threshold = 100; + par->init(par); + auok190x_identify(par); + + platform_set_drvdata(pdev, info); + + ret = register_framebuffer(info); + if (ret < 0) + goto err_regfb; + + ret = sysfs_create_group(&info->device->kobj, &auok190x_attr_group); + if (ret) + goto err_sysfs; + + dev_info(info->device, "fb%d: %dx%d using %dK of video memory\n", + info->node, info->var.xres, info->var.yres, + videomemorysize >> 10); + + /* increase autosuspend_delay when we use alternative methods + * for runtime_pm + */ + par->autosuspend_delay = (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) + ? 1000 : 200; + + pm_runtime_set_active(info->device); + pm_runtime_enable(info->device); + pm_runtime_set_autosuspend_delay(info->device, par->autosuspend_delay); + pm_runtime_use_autosuspend(info->device); + + return 0; + +err_sysfs: + unregister_framebuffer(info); +err_regfb: + fb_dealloc_cmap(&info->cmap); +err_cmap: + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); +err_defio: + vfree((void *)info->screen_base); +err_irq: + auok190x_power(par, 0); +err_gpio3: + gpio_free(board->gpio_nrst); +err_gpio2: + gpio_free(board->gpio_nsleep); +err_gpio1: + board->cleanup(par); +err_board: + regulator_put(par->regulator); +err_reg: + framebuffer_release(info); + + return ret; +} +EXPORT_SYMBOL_GPL(auok190x_common_probe); + +int __devexit auok190x_common_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + pm_runtime_disable(info->device); + + sysfs_remove_group(&info->device->kobj, &auok190x_attr_group); + + unregister_framebuffer(info); + + fb_dealloc_cmap(&info->cmap); + + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); + + vfree((void *)info->screen_base); + + auok190x_power(par, 0); + + gpio_free(board->gpio_nrst); + gpio_free(board->gpio_nsleep); + + board->cleanup(par); + + regulator_put(par->regulator); + + framebuffer_release(info); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_common_remove); + +MODULE_DESCRIPTION("Common code for AUO-K190X controllers"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k190x.h b/drivers/video/auo_k190x.h new file mode 100644 index 000000000000..e35af1f51b28 --- /dev/null +++ b/drivers/video/auo_k190x.h @@ -0,0 +1,129 @@ +/* + * Private common definitions for AUO-K190X framebuffer drivers + * + * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> + * + * 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. + */ + +/* + * I80 interface specific defines + */ + +#define AUOK190X_I80_CS 0x01 +#define AUOK190X_I80_DC 0x02 +#define AUOK190X_I80_WR 0x03 +#define AUOK190X_I80_OE 0x04 + +/* + * AUOK190x commands, common to both controllers + */ + +#define AUOK190X_CMD_INIT 0x0000 +#define AUOK190X_CMD_STANDBY 0x0001 +#define AUOK190X_CMD_WAKEUP 0x0002 +#define AUOK190X_CMD_TCON_RESET 0x0003 +#define AUOK190X_CMD_DATA_STOP 0x1002 +#define AUOK190X_CMD_LUT_START 0x1003 +#define AUOK190X_CMD_DISP_REFRESH 0x1004 +#define AUOK190X_CMD_DISP_RESET 0x1005 +#define AUOK190X_CMD_PRE_DISPLAY_START 0x100D +#define AUOK190X_CMD_PRE_DISPLAY_STOP 0x100F +#define AUOK190X_CMD_FLASH_W 0x2000 +#define AUOK190X_CMD_FLASH_E 0x2001 +#define AUOK190X_CMD_FLASH_STS 0x2002 +#define AUOK190X_CMD_FRAMERATE 0x3000 +#define AUOK190X_CMD_READ_VERSION 0x4000 +#define AUOK190X_CMD_READ_STATUS 0x4001 +#define AUOK190X_CMD_READ_LUT 0x4003 +#define AUOK190X_CMD_DRIVERTIMING 0x5000 +#define AUOK190X_CMD_LBALANCE 0x5001 +#define AUOK190X_CMD_AGINGMODE 0x6000 +#define AUOK190X_CMD_AGINGEXIT 0x6001 + +/* + * Common settings for AUOK190X_CMD_INIT + */ + +#define AUOK190X_INIT_DATA_FILTER (0 << 12) +#define AUOK190X_INIT_DATA_BYPASS (1 << 12) +#define AUOK190X_INIT_INVERSE_WHITE (0 << 9) +#define AUOK190X_INIT_INVERSE_BLACK (1 << 9) +#define AUOK190X_INIT_SCAN_DOWN (0 << 1) +#define AUOK190X_INIT_SCAN_UP (1 << 1) +#define AUOK190X_INIT_SHIFT_LEFT (0 << 0) +#define AUOK190X_INIT_SHIFT_RIGHT (1 << 0) + +/* Common bits to pixels + * Mode 15-12 11-8 7-4 3-0 + * format0 4 3 2 1 + * format1 3 4 1 2 + */ + +#define AUOK190X_INIT_FORMAT0 0 +#define AUOK190X_INIT_FORMAT1 (1 << 6) + +/* + * settings for AUOK190X_CMD_RESET + */ + +#define AUOK190X_RESET_TCON (0 << 0) +#define AUOK190X_RESET_NORMAL (1 << 0) +#define AUOK190X_RESET_PON (1 << 1) + +/* + * AUOK190X_CMD_VERSION + */ + +#define AUOK190X_VERSION_TEMP_MASK (0x1ff) +#define AUOK190X_VERSION_EPD_MASK (0xff) +#define AUOK190X_VERSION_SIZE_INT(_val) ((_val & 0xfc00) >> 10) +#define AUOK190X_VERSION_SIZE_FLOAT(_val) ((_val & 0x3c0) >> 6) +#define AUOK190X_VERSION_MODEL(_val) (_val & 0x3f) +#define AUOK190X_VERSION_LUT(_val) (_val & 0xff) +#define AUOK190X_VERSION_TCON(_val) ((_val & 0xff00) >> 8) + +/* + * update modes for CMD_PARTIALDISP on K1900 and CMD_DDMA on K1901 + */ + +#define AUOK190X_UPDATE_MODE(_res) ((_res & 0x7) << 12) +#define AUOK190X_UPDATE_NONFLASH (1 << 15) + +/* + * track panel specific parameters for common init + */ + +struct auok190x_init_data { + char *id; + struct auok190x_board *board; + + void (*update_partial)(struct auok190xfb_par *par, u16 y1, u16 y2); + void (*update_all)(struct auok190xfb_par *par); + bool (*need_refresh)(struct auok190xfb_par *par); + void (*init)(struct auok190xfb_par *par); +}; + + +extern void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data); +extern int auok190x_send_command(struct auok190xfb_par *par, u16 data); +extern void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); +extern int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); +extern void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, + u16 cmd, int argc, u16 *argv, + int size, u16 *data); +extern int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, + u16 *data); +extern int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); + +extern int auok190x_common_probe(struct platform_device *pdev, + struct auok190x_init_data *init); +extern int auok190x_common_remove(struct platform_device *pdev); + +extern const struct dev_pm_ops auok190x_pm; diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c index 1a268a294478..33ea874c87d2 100644 --- a/drivers/video/bfin_adv7393fb.c +++ b/drivers/video/bfin_adv7393fb.c @@ -414,14 +414,14 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n"); ret = -EBUSY; - goto out_8; + goto free_fbdev; } } if (peripheral_request_list(ppi_pins, DRIVER_NAME)) { dev_err(&client->dev, "requesting PPI peripheral failed\n"); ret = -EFAULT; - goto out_8; + goto free_gpio; } fbdev->fb_mem = @@ -432,7 +432,7 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, dev_err(&client->dev, "couldn't allocate dma buffer (%d bytes)\n", (u32) fbdev->fb_len); ret = -ENOMEM; - goto out_7; + goto free_ppi_pins; } fbdev->info.screen_base = (void *)fbdev->fb_mem; @@ -464,27 +464,27 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (!fbdev->info.pseudo_palette) { dev_err(&client->dev, "failed to allocate pseudo_palette\n"); ret = -ENOMEM; - goto out_6; + goto free_fb_mem; } if (fb_alloc_cmap(&fbdev->info.cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { dev_err(&client->dev, "failed to allocate colormap (%d entries)\n", BFIN_LCD_NBR_PALETTE_ENTRIES); ret = -EFAULT; - goto out_5; + goto free_palette; } if (request_dma(CH_PPI, "BF5xx_PPI_DMA") < 0) { dev_err(&client->dev, "unable to request PPI DMA\n"); ret = -EFAULT; - goto out_4; + goto free_cmap; } if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, 0, "PPI ERROR", fbdev) < 0) { dev_err(&client->dev, "unable to request PPI ERROR IRQ\n"); ret = -EFAULT; - goto out_3; + goto free_ch_ppi; } fbdev->open = 0; @@ -494,14 +494,14 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "i2c attach: init error\n"); - goto out_1; + goto free_irq_ppi; } if (register_framebuffer(&fbdev->info) < 0) { dev_err(&client->dev, "unable to register framebuffer\n"); ret = -EFAULT; - goto out_1; + goto free_irq_ppi; } dev_info(&client->dev, "fb%d: %s frame buffer device\n", @@ -512,7 +512,7 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (!entry) { dev_err(&client->dev, "unable to create /proc entry\n"); ret = -EFAULT; - goto out_0; + goto free_fb; } entry->read_proc = adv7393_read_proc; @@ -521,22 +521,25 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, return 0; - out_0: +free_fb: unregister_framebuffer(&fbdev->info); - out_1: +free_irq_ppi: free_irq(IRQ_PPI_ERROR, fbdev); - out_3: +free_ch_ppi: free_dma(CH_PPI); - out_4: - dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, - fbdev->dma_handle); - out_5: +free_cmap: fb_dealloc_cmap(&fbdev->info.cmap); - out_6: +free_palette: kfree(fbdev->info.pseudo_palette); - out_7: +free_fb_mem: + dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, + fbdev->dma_handle); +free_ppi_pins: peripheral_free_list(ppi_pins); - out_8: +free_gpio: + if (ANOMALY_05000400) + gpio_free(P_IDENT(P_PPI0_FS3)); +free_fbdev: kfree(fbdev); return ret; diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c index f56699d8122a..eae46f6457e2 100644 --- a/drivers/video/cobalt_lcdfb.c +++ b/drivers/video/cobalt_lcdfb.c @@ -1,7 +1,8 @@ /* - * Cobalt server LCD frame buffer driver. + * Cobalt/SEAD3 LCD frame buffer driver. * * Copyright (C) 2008 Yoichi Yuasa <yuasa@linux-mips.org> + * Copyright (C) 2012 MIPS Technologies, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -62,6 +63,7 @@ #define LCD_CUR_POS(x) ((x) & LCD_CUR_POS_MASK) #define LCD_TEXT_POS(x) ((x) | LCD_TEXT_MODE) +#ifdef CONFIG_MIPS_COBALT static inline void lcd_write_control(struct fb_info *info, u8 control) { writel((u32)control << 24, info->screen_base); @@ -81,6 +83,47 @@ static inline u8 lcd_read_data(struct fb_info *info) { return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24; } +#else + +#define LCD_CTL 0x00 +#define LCD_DATA 0x08 +#define CPLD_STATUS 0x10 +#define CPLD_DATA 0x18 + +static inline void cpld_wait(struct fb_info *info) +{ + do { + } while (readl(info->screen_base + CPLD_STATUS) & 1); +} + +static inline void lcd_write_control(struct fb_info *info, u8 control) +{ + cpld_wait(info); + writel(control, info->screen_base + LCD_CTL); +} + +static inline u8 lcd_read_control(struct fb_info *info) +{ + cpld_wait(info); + readl(info->screen_base + LCD_CTL); + cpld_wait(info); + return readl(info->screen_base + CPLD_DATA) & 0xff; +} + +static inline void lcd_write_data(struct fb_info *info, u8 data) +{ + cpld_wait(info); + writel(data, info->screen_base + LCD_DATA); +} + +static inline u8 lcd_read_data(struct fb_info *info) +{ + cpld_wait(info); + readl(info->screen_base + LCD_DATA); + cpld_wait(info); + return readl(info->screen_base + CPLD_DATA) & 0xff; +} +#endif static int lcd_busy_wait(struct fb_info *info) { diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c index f8babbeee275..345d96230978 100644 --- a/drivers/video/ep93xx-fb.c +++ b/drivers/video/ep93xx-fb.c @@ -507,16 +507,16 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) err = fb_alloc_cmap(&info->cmap, 256, 0); if (err) - goto failed; + goto failed_cmap; err = ep93xxfb_alloc_videomem(info); if (err) - goto failed; + goto failed_videomem; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { err = -ENXIO; - goto failed; + goto failed_resource; } /* @@ -532,7 +532,7 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) fbi->mmio_base = ioremap(res->start, resource_size(res)); if (!fbi->mmio_base) { err = -ENXIO; - goto failed; + goto failed_resource; } strcpy(info->fix.id, pdev->name); @@ -553,24 +553,24 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) if (err == 0) { dev_err(info->dev, "No suitable video mode found\n"); err = -EINVAL; - goto failed; + goto failed_mode; } if (mach_info->setup) { err = mach_info->setup(pdev); if (err) - return err; + goto failed_mode; } err = ep93xxfb_check_var(&info->var, info); if (err) - goto failed; + goto failed_check; fbi->clk = clk_get(info->dev, NULL); if (IS_ERR(fbi->clk)) { err = PTR_ERR(fbi->clk); fbi->clk = NULL; - goto failed; + goto failed_check; } ep93xxfb_set_par(info); @@ -585,15 +585,17 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) return 0; failed: - if (fbi->clk) - clk_put(fbi->clk); - if (fbi->mmio_base) - iounmap(fbi->mmio_base); - ep93xxfb_dealloc_videomem(info); - if (&info->cmap) - fb_dealloc_cmap(&info->cmap); + clk_put(fbi->clk); +failed_check: if (fbi->mach_info->teardown) fbi->mach_info->teardown(pdev); +failed_mode: + iounmap(fbi->mmio_base); +failed_resource: + ep93xxfb_dealloc_videomem(info); +failed_videomem: + fb_dealloc_cmap(&info->cmap); +failed_cmap: kfree(info); platform_set_drvdata(pdev, NULL); diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c index 2a4481cf260c..a36b2d28280e 100644 --- a/drivers/video/exynos/exynos_dp_core.c +++ b/drivers/video/exynos/exynos_dp_core.c @@ -21,14 +21,14 @@ #include <video/exynos_dp.h> -#include <plat/cpu.h> - #include "exynos_dp_core.h" static int exynos_dp_init_dp(struct exynos_dp_device *dp) { exynos_dp_reset(dp); + exynos_dp_swreset(dp); + /* SW defined function Normal operation */ exynos_dp_enable_sw_function(dp); @@ -478,7 +478,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) int lane_count; u8 buf[5]; - u8 *adjust_request; + u8 adjust_request[2]; u8 voltage_swing; u8 pre_emphasis; u8 training_lane; @@ -493,8 +493,8 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) /* set training pattern 2 for EQ */ exynos_dp_set_training_pattern(dp, TRAINING_PTN2); - adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 - - DPCD_ADDR_LANE0_1_STATUS); + adjust_request[0] = link_status[4]; + adjust_request[1] = link_status[5]; exynos_dp_get_adjust_train(dp, adjust_request); @@ -566,7 +566,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) u8 buf[5]; u32 reg; - u8 *adjust_request; + u8 adjust_request[2]; udelay(400); @@ -575,8 +575,8 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) lane_count = dp->link_train.lane_count; if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { - adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 - - DPCD_ADDR_LANE0_1_STATUS); + adjust_request[0] = link_status[4]; + adjust_request[1] = link_status[5]; if (exynos_dp_channel_eq_ok(link_status, lane_count) == 0) { /* traing pattern Set to Normal */ @@ -770,7 +770,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp, return -ETIMEDOUT; } - mdelay(100); + udelay(1); } /* Set to use the register calculated M/N video */ @@ -804,7 +804,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp, return -ETIMEDOUT; } - mdelay(100); + mdelay(1); } if (retval != 0) @@ -860,7 +860,8 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) return -EINVAL; } - dp = kzalloc(sizeof(struct exynos_dp_device), GFP_KERNEL); + dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), + GFP_KERNEL); if (!dp) { dev_err(&pdev->dev, "no memory for device data\n"); return -ENOMEM; @@ -871,8 +872,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) dp->clock = clk_get(&pdev->dev, "dp"); if (IS_ERR(dp->clock)) { dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(dp->clock); - goto err_dp; + return PTR_ERR(dp->clock); } clk_enable(dp->clock); @@ -884,35 +884,25 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) goto err_clock; } - res = request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev)); - if (!res) { - dev_err(&pdev->dev, "failed to request registers region\n"); - ret = -EINVAL; - goto err_clock; - } - - dp->res = res; - - dp->reg_base = ioremap(res->start, resource_size(res)); + dp->reg_base = devm_request_and_ioremap(&pdev->dev, res); if (!dp->reg_base) { dev_err(&pdev->dev, "failed to ioremap\n"); ret = -ENOMEM; - goto err_req_region; + goto err_clock; } dp->irq = platform_get_irq(pdev, 0); if (!dp->irq) { dev_err(&pdev->dev, "failed to get irq\n"); ret = -ENODEV; - goto err_ioremap; + goto err_clock; } - ret = request_irq(dp->irq, exynos_dp_irq_handler, 0, - "exynos-dp", dp); + ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, + "exynos-dp", dp); if (ret) { dev_err(&pdev->dev, "failed to request irq\n"); - goto err_ioremap; + goto err_clock; } dp->video_info = pdata->video_info; @@ -924,7 +914,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) ret = exynos_dp_detect_hpd(dp); if (ret) { dev_err(&pdev->dev, "unable to detect hpd\n"); - goto err_irq; + goto err_clock; } exynos_dp_handle_edid(dp); @@ -933,7 +923,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) dp->video_info->link_rate); if (ret) { dev_err(&pdev->dev, "unable to do link train\n"); - goto err_irq; + goto err_clock; } exynos_dp_enable_scramble(dp, 1); @@ -947,23 +937,15 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) ret = exynos_dp_config_video(dp, dp->video_info); if (ret) { dev_err(&pdev->dev, "unable to config video\n"); - goto err_irq; + goto err_clock; } platform_set_drvdata(pdev, dp); return 0; -err_irq: - free_irq(dp->irq, dp); -err_ioremap: - iounmap(dp->reg_base); -err_req_region: - release_mem_region(res->start, resource_size(res)); err_clock: clk_put(dp->clock); -err_dp: - kfree(dp); return ret; } @@ -976,16 +958,9 @@ static int __devexit exynos_dp_remove(struct platform_device *pdev) if (pdata && pdata->phy_exit) pdata->phy_exit(); - free_irq(dp->irq, dp); - iounmap(dp->reg_base); - clk_disable(dp->clock); clk_put(dp->clock); - release_mem_region(dp->res->start, resource_size(dp->res)); - - kfree(dp); - return 0; } diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h index 90ceaca0fa24..1e0f998e0c9f 100644 --- a/drivers/video/exynos/exynos_dp_core.h +++ b/drivers/video/exynos/exynos_dp_core.h @@ -26,7 +26,6 @@ struct link_train { struct exynos_dp_device { struct device *dev; - struct resource *res; struct clk *clock; unsigned int irq; void __iomem *reg_base; @@ -39,8 +38,10 @@ struct exynos_dp_device { void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable); void exynos_dp_stop_video(struct exynos_dp_device *dp); void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable); +void exynos_dp_init_analog_param(struct exynos_dp_device *dp); void exynos_dp_init_interrupt(struct exynos_dp_device *dp); void exynos_dp_reset(struct exynos_dp_device *dp); +void exynos_dp_swreset(struct exynos_dp_device *dp); void exynos_dp_config_interrupt(struct exynos_dp_device *dp); u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp); void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable); diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c index 6548afa0e3d2..6ce76d56c3a1 100644 --- a/drivers/video/exynos/exynos_dp_reg.c +++ b/drivers/video/exynos/exynos_dp_reg.c @@ -16,8 +16,6 @@ #include <video/exynos_dp.h> -#include <plat/cpu.h> - #include "exynos_dp_core.h" #include "exynos_dp_reg.h" @@ -65,6 +63,28 @@ void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable) writel(reg, dp->reg_base + EXYNOS_DP_LANE_MAP); } +void exynos_dp_init_analog_param(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = TX_TERMINAL_CTRL_50_OHM; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_1); + + reg = SEL_24M | TX_DVDD_BIT_1_0625V; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_2); + + reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_3); + + reg = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM | + TX_CUR1_2X | TX_CUR_8_MA; + writel(reg, dp->reg_base + EXYNOS_DP_PLL_FILTER_CTL_1); + + reg = CH3_AMP_400_MV | CH2_AMP_400_MV | + CH1_AMP_400_MV | CH0_AMP_400_MV; + writel(reg, dp->reg_base + EXYNOS_DP_TX_AMP_TUNING_CTL); +} + void exynos_dp_init_interrupt(struct exynos_dp_device *dp) { /* Set interrupt pin assertion polarity as high */ @@ -89,8 +109,6 @@ void exynos_dp_reset(struct exynos_dp_device *dp) { u32 reg; - writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET); - exynos_dp_stop_video(dp); exynos_dp_enable_video_mute(dp, 0); @@ -131,9 +149,15 @@ void exynos_dp_reset(struct exynos_dp_device *dp) writel(0x00000101, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + exynos_dp_init_analog_param(dp); exynos_dp_init_interrupt(dp); } +void exynos_dp_swreset(struct exynos_dp_device *dp) +{ + writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET); +} + void exynos_dp_config_interrupt(struct exynos_dp_device *dp) { u32 reg; @@ -271,6 +295,7 @@ void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, void exynos_dp_init_analog_func(struct exynos_dp_device *dp) { u32 reg; + int timeout_loop = 0; exynos_dp_set_analog_power_down(dp, POWER_ALL, 0); @@ -282,9 +307,19 @@ void exynos_dp_init_analog_func(struct exynos_dp_device *dp) writel(reg, dp->reg_base + EXYNOS_DP_DEBUG_CTL); /* Power up PLL */ - if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) + if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { exynos_dp_set_pll_power_down(dp, 0); + while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + timeout_loop++; + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "failed to get pll lock status\n"); + return; + } + usleep_range(10, 20); + } + } + /* Enable Serdes FIFO function and Link symbol clock domain module */ reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h index 42f608e2a43e..125b27cd57ae 100644 --- a/drivers/video/exynos/exynos_dp_reg.h +++ b/drivers/video/exynos/exynos_dp_reg.h @@ -24,6 +24,12 @@ #define EXYNOS_DP_LANE_MAP 0x35C +#define EXYNOS_DP_ANALOG_CTL_1 0x370 +#define EXYNOS_DP_ANALOG_CTL_2 0x374 +#define EXYNOS_DP_ANALOG_CTL_3 0x378 +#define EXYNOS_DP_PLL_FILTER_CTL_1 0x37C +#define EXYNOS_DP_TX_AMP_TUNING_CTL 0x380 + #define EXYNOS_DP_AUX_HW_RETRY_CTL 0x390 #define EXYNOS_DP_COMMON_INT_STA_1 0x3C4 @@ -166,6 +172,29 @@ #define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) #define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) +/* EXYNOS_DP_ANALOG_CTL_1 */ +#define TX_TERMINAL_CTRL_50_OHM (0x1 << 4) + +/* EXYNOS_DP_ANALOG_CTL_2 */ +#define SEL_24M (0x1 << 3) +#define TX_DVDD_BIT_1_0625V (0x4 << 0) + +/* EXYNOS_DP_ANALOG_CTL_3 */ +#define DRIVE_DVDD_BIT_1_0625V (0x4 << 5) +#define VCO_BIT_600_MICRO (0x5 << 0) + +/* EXYNOS_DP_PLL_FILTER_CTL_1 */ +#define PD_RING_OSC (0x1 << 6) +#define AUX_TERMINAL_CTRL_50_OHM (0x2 << 4) +#define TX_CUR1_2X (0x1 << 2) +#define TX_CUR_8_MA (0x2 << 0) + +/* EXYNOS_DP_TX_AMP_TUNING_CTL */ +#define CH3_AMP_400_MV (0x0 << 24) +#define CH2_AMP_400_MV (0x0 << 16) +#define CH1_AMP_400_MV (0x0 << 8) +#define CH0_AMP_400_MV (0x0 << 0) + /* EXYNOS_DP_AUX_HW_RETRY_CTL */ #define AUX_BIT_PERIOD_EXPECTED_DELAY(x) (((x) & 0x7) << 8) #define AUX_HW_RETRY_INTERVAL_MASK (0x3 << 3) diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c index 557091dc0e97..6c1f5c314a42 100644 --- a/drivers/video/exynos/exynos_mipi_dsi.c +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -58,7 +58,7 @@ static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device } static struct regulator_bulk_data supplies[] = { - { .supply = "vdd10", }, + { .supply = "vdd11", }, { .supply = "vdd18", }, }; @@ -102,6 +102,8 @@ static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim) /* set display timing. */ exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); + exynos_mipi_dsi_init_interrupt(dsim); + /* * data from Display controller(FIMD) is transferred in video mode * but in case of command mode, all settigs is updated to registers. @@ -413,27 +415,30 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) goto err_platform_get_irq; } + init_completion(&dsim_wr_comp); + init_completion(&dsim_rd_comp); + platform_set_drvdata(pdev, dsim); + ret = request_irq(dsim->irq, exynos_mipi_dsi_interrupt_handler, - IRQF_SHARED, pdev->name, dsim); + IRQF_SHARED, dev_name(&pdev->dev), dsim); if (ret != 0) { dev_err(&pdev->dev, "failed to request dsim irq\n"); ret = -EINVAL; goto err_bind; } - init_completion(&dsim_wr_comp); - init_completion(&dsim_rd_comp); - - /* enable interrupt */ + /* enable interrupts */ exynos_mipi_dsi_init_interrupt(dsim); /* initialize mipi-dsi client(lcd panel). */ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe) dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev); - /* in case that mipi got enabled at bootloader. */ - if (dsim_pd->enabled) - goto out; + /* in case mipi-dsi has been enabled by bootloader */ + if (dsim_pd->enabled) { + exynos_mipi_regulator_enable(dsim); + goto done; + } /* lcd panel power on. */ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on) @@ -453,12 +458,11 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) dsim->suspended = false; -out: +done: platform_set_drvdata(pdev, dsim); - dev_dbg(&pdev->dev, "mipi-dsi driver(%s mode) has been probed.\n", - (dsim_config->e_interface == DSIM_COMMAND) ? - "CPU" : "RGB"); + dev_dbg(&pdev->dev, "%s() completed sucessfuly (%s mode)\n", __func__, + dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB"); return 0; @@ -515,10 +519,10 @@ static int __devexit exynos_mipi_dsi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int exynos_mipi_dsi_suspend(struct platform_device *pdev, - pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int exynos_mipi_dsi_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; @@ -544,8 +548,9 @@ static int exynos_mipi_dsi_suspend(struct platform_device *pdev, return 0; } -static int exynos_mipi_dsi_resume(struct platform_device *pdev) +static int exynos_mipi_dsi_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; @@ -577,19 +582,19 @@ static int exynos_mipi_dsi_resume(struct platform_device *pdev) return 0; } -#else -#define exynos_mipi_dsi_suspend NULL -#define exynos_mipi_dsi_resume NULL #endif +static const struct dev_pm_ops exynos_mipi_dsi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_mipi_dsi_suspend, exynos_mipi_dsi_resume) +}; + static struct platform_driver exynos_mipi_dsi_driver = { .probe = exynos_mipi_dsi_probe, .remove = __devexit_p(exynos_mipi_dsi_remove), - .suspend = exynos_mipi_dsi_suspend, - .resume = exynos_mipi_dsi_resume, .driver = { .name = "exynos-mipi-dsim", .owner = THIS_MODULE, + .pm = &exynos_mipi_dsi_pm_ops, }, }; diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c index 14909c1d3832..47b533a183be 100644 --- a/drivers/video/exynos/exynos_mipi_dsi_common.c +++ b/drivers/video/exynos/exynos_mipi_dsi_common.c @@ -76,33 +76,25 @@ static unsigned int dpll_table[15] = { irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id) { - unsigned int intsrc = 0; - unsigned int intmsk = 0; - struct mipi_dsim_device *dsim = NULL; - - dsim = dev_id; - if (!dsim) { - dev_dbg(dsim->dev, KERN_ERR "%s:error: wrong parameter\n", - __func__); - return IRQ_HANDLED; + struct mipi_dsim_device *dsim = dev_id; + unsigned int intsrc, intmsk; + + if (dsim == NULL) { + dev_err(dsim->dev, "%s: wrong parameter\n", __func__); + return IRQ_NONE; } intsrc = exynos_mipi_dsi_read_interrupt(dsim); intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim); + intmsk = ~intmsk & intsrc; - intmsk = ~(intmsk) & intsrc; - - switch (intmsk) { - case INTMSK_RX_DONE: + if (intsrc & INTMSK_RX_DONE) { complete(&dsim_rd_comp); dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n"); - break; - case INTMSK_FIFO_EMPTY: + } + if (intsrc & INTMSK_FIFO_EMPTY) { complete(&dsim_wr_comp); dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n"); - break; - default: - break; } exynos_mipi_dsi_clear_interrupt(dsim, intmsk); @@ -738,11 +730,11 @@ int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, if (dsim_config->auto_vertical_cnt == 0) { exynos_mipi_dsi_set_main_disp_vporch(dsim, dsim_config->cmd_allow, - timing->upper_margin, - timing->lower_margin); + timing->lower_margin, + timing->upper_margin); exynos_mipi_dsi_set_main_disp_hporch(dsim, - timing->left_margin, - timing->right_margin); + timing->right_margin, + timing->left_margin); exynos_mipi_dsi_set_main_disp_sync_area(dsim, timing->vsync_len, timing->hsync_len); diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c index 4aa9ac6218bf..05d080b63bc0 100644 --- a/drivers/video/exynos/s6e8ax0.c +++ b/drivers/video/exynos/s6e8ax0.c @@ -293,9 +293,20 @@ static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8 }; + static const unsigned char data_to_send_panel_reverse[] = { + 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, + 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, + 0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1 + }; - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); + if (lcd->dsim_dev->panel_reverse) + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send_panel_reverse, + ARRAY_SIZE(data_to_send_panel_reverse)); + else + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); } static void s6e8ax0_display_cond(struct s6e8ax0 *lcd) diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index c27e153d8882..1ddeb11659d4 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -23,7 +23,7 @@ #include <linux/rmap.h> #include <linux/pagemap.h> -struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) +static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) { void *screen_base = (void __force *) info->screen_base; struct page *page; @@ -107,6 +107,10 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, /* protect against the workqueue changing the page list */ mutex_lock(&fbdefio->lock); + /* first write in this cycle, notify the driver */ + if (fbdefio->first_io && list_empty(&fbdefio->pagelist)) + fbdefio->first_io(info); + /* * We want the page to remain locked from ->page_mkwrite until * the PTE is marked dirty to avoid page_mkclean() being called diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 67afa9c2289d..a55e3669d135 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -80,6 +80,8 @@ EXPORT_SYMBOL(framebuffer_alloc); */ void framebuffer_release(struct fb_info *info) { + if (!info) + return; kfree(info->apertures); kfree(info); } diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 6af3f16754f0..458c00664ade 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -834,7 +834,6 @@ static void update_lcdc(struct fb_info *info) diu_ops.set_pixel_clock(var->pixclock); out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ - out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */ out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */ out_be32(&hw->plut, 0x01F5F666); diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index 02fd2263610c..bdcbfbae2777 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c @@ -680,6 +680,7 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, + dinfo->fb.size); if (!dinfo->aperture.virtual) { ERR_MSG("Cannot remap FB region.\n"); + agp_backend_release(bridge); cleanup(dinfo); return -ENODEV; } @@ -689,6 +690,7 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, INTEL_REG_SIZE); if (!dinfo->mmio_base) { ERR_MSG("Cannot remap MMIO region.\n"); + agp_backend_release(bridge); cleanup(dinfo); return -ENODEV; } diff --git a/drivers/video/mb862xx/mb862xx-i2c.c b/drivers/video/mb862xx/mb862xx-i2c.c index 273769bb8deb..c87e17afb3e2 100644 --- a/drivers/video/mb862xx/mb862xx-i2c.c +++ b/drivers/video/mb862xx/mb862xx-i2c.c @@ -68,7 +68,7 @@ static int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last) return 1; } -void mb862xx_i2c_stop(struct i2c_adapter *adap) +static void mb862xx_i2c_stop(struct i2c_adapter *adap) { struct mb862xxfb_par *par = adap->algo_data; diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c index 11a7a333701d..00ce1f34b496 100644 --- a/drivers/video/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/mb862xx/mb862xxfbdrv.c @@ -579,7 +579,7 @@ static ssize_t mb862xxfb_show_dispregs(struct device *dev, static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL); -irqreturn_t mb862xx_intr(int irq, void *dev_id) +static irqreturn_t mb862xx_intr(int irq, void *dev_id) { struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; unsigned long reg_ist, mask; diff --git a/drivers/video/mbx/mbxfb.c b/drivers/video/mbx/mbxfb.c index 55bf6196b7a0..ab0a8e527333 100644 --- a/drivers/video/mbx/mbxfb.c +++ b/drivers/video/mbx/mbxfb.c @@ -950,7 +950,7 @@ static int __devinit mbxfb_probe(struct platform_device *dev) mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr, res_size(mfbi->fb_req)); - if (!mfbi->reg_virt_addr) { + if (!mfbi->fb_virt_addr) { dev_err(&dev->dev, "failed to ioremap frame buffer\n"); ret = -EINVAL; goto err4; diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 6c6bc578d0fc..abbe691047bd 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -889,6 +889,18 @@ static int __devexit mxsfb_remove(struct platform_device *pdev) return 0; } +static void mxsfb_shutdown(struct platform_device *pdev) +{ + struct fb_info *fb_info = platform_get_drvdata(pdev); + struct mxsfb_info *host = to_imxfb_host(fb_info); + + /* + * Force stop the LCD controller as keeping it running during reboot + * might interfere with the BootROM's boot mode pads sampling. + */ + writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR); +} + static struct platform_device_id mxsfb_devtype[] = { { .name = "imx23-fb", @@ -905,6 +917,7 @@ MODULE_DEVICE_TABLE(platform, mxsfb_devtype); static struct platform_driver mxsfb_driver = { .probe = mxsfb_probe, .remove = __devexit_p(mxsfb_remove), + .shutdown = mxsfb_shutdown, .id_table = mxsfb_devtype, .driver = { .name = DRIVER_NAME, diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 1e7536d9a8fc..b48f95f0dfe2 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -39,14 +39,6 @@ config FB_OMAP_LCD_MIPID the Mobile Industry Processor Interface DBI-C/DCS specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3) -config FB_OMAP_BOOTLOADER_INIT - bool "Check bootloader initialization" - depends on FB_OMAP - help - Say Y here if you want to enable checking if the bootloader has - already initialized the display controller. In this case the - driver will skip the initialization. - config FB_OMAP_CONSISTENT_DMA_SIZE int "Consistent DMA memory size (MB)" depends on FB_OMAP diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 74e7cf078505..ad741c3d1ae1 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -739,12 +739,6 @@ static void acx_panel_set_timings(struct omap_dss_device *dssdev, } } -static void acx_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static int acx_panel_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -762,7 +756,6 @@ static struct omap_dss_driver acx_panel_driver = { .resume = acx_panel_resume, .set_timings = acx_panel_set_timings, - .get_timings = acx_panel_get_timings, .check_timings = acx_panel_check_timings, .get_recommended_bpp = acx_get_recommended_bpp, diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 30fe4dfeb227..e42f9dc22123 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -386,6 +386,106 @@ static struct panel_config generic_dpi_panels[] = { .name = "innolux_at080tn52", }, + + /* Mitsubishi AA084SB01 */ + { + { + .x_res = 800, + .y_res = 600, + .pixel_clock = 40000, + + .hsw = 1, + .hfp = 254, + .hbp = 1, + + .vsw = 1, + .vfp = 26, + .vbp = 1, + }, + .config = OMAP_DSS_LCD_TFT, + .name = "mitsubishi_aa084sb01", + }, + /* EDT ET0500G0DH6 */ + { + { + .x_res = 800, + .y_res = 480, + .pixel_clock = 33260, + + .hsw = 128, + .hfp = 216, + .hbp = 40, + + .vsw = 2, + .vfp = 35, + .vbp = 10, + }, + .config = OMAP_DSS_LCD_TFT, + .name = "edt_et0500g0dh6", + }, + + /* Prime-View PD050VL1 */ + { + { + .x_res = 640, + .y_res = 480, + + .pixel_clock = 25000, + + .hsw = 96, + .hfp = 18, + .hbp = 46, + + .vsw = 2, + .vfp = 10, + .vbp = 33, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pd050vl1", + }, + + /* Prime-View PM070WL4 */ + { + { + .x_res = 800, + .y_res = 480, + + .pixel_clock = 32000, + + .hsw = 128, + .hfp = 42, + .hbp = 86, + + .vsw = 2, + .vfp = 10, + .vbp = 33, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pm070wl4", + }, + + /* Prime-View PD104SLF */ + { + { + .x_res = 800, + .y_res = 600, + + .pixel_clock = 40000, + + .hsw = 128, + .hfp = 42, + .hbp = 86, + + .vsw = 4, + .vfp = 1, + .vbp = 23, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pd104slf", + }, }; struct panel_drv_data { @@ -549,12 +649,6 @@ static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev, dpi_set_timings(dssdev, timings); } -static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -571,7 +665,6 @@ static struct omap_dss_driver dpi_driver = { .resume = generic_dpi_panel_resume, .set_timings = generic_dpi_panel_set_timings, - .get_timings = generic_dpi_panel_get_timings, .check_timings = generic_dpi_panel_check_timings, .driver = { diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c index dc9408dc93d1..4a34cdc1371b 100644 --- a/drivers/video/omap2/displays/panel-n8x0.c +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -610,12 +610,6 @@ static int n8x0_panel_resume(struct omap_dss_device *dssdev) return 0; } -static void n8x0_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void n8x0_panel_get_resolution(struct omap_dss_device *dssdev, u16 *xres, u16 *yres) { @@ -678,8 +672,6 @@ static struct omap_dss_driver n8x0_panel_driver = { .get_resolution = n8x0_panel_get_resolution, .get_recommended_bpp = omapdss_default_get_recommended_bpp, - .get_timings = n8x0_panel_get_timings, - .driver = { .name = "n8x0_panel", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index b2dd88b48420..2ce9992f403b 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -30,7 +30,6 @@ #include <linux/gpio.h> #include <linux/workqueue.h> #include <linux/slab.h> -#include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <video/omapdss.h> @@ -55,73 +54,6 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable); static int taal_panel_reset(struct omap_dss_device *dssdev); -struct panel_regulator { - struct regulator *regulator; - const char *name; - int min_uV; - int max_uV; -}; - -static void free_regulators(struct panel_regulator *regulators, int n) -{ - int i; - - for (i = 0; i < n; i++) { - /* disable/put in reverse order */ - regulator_disable(regulators[n - i - 1].regulator); - regulator_put(regulators[n - i - 1].regulator); - } -} - -static int init_regulators(struct omap_dss_device *dssdev, - struct panel_regulator *regulators, int n) -{ - int r, i, v; - - for (i = 0; i < n; i++) { - struct regulator *reg; - - reg = regulator_get(&dssdev->dev, regulators[i].name); - if (IS_ERR(reg)) { - dev_err(&dssdev->dev, "failed to get regulator %s\n", - regulators[i].name); - r = PTR_ERR(reg); - goto err; - } - - /* FIXME: better handling of fixed vs. variable regulators */ - v = regulator_get_voltage(reg); - if (v < regulators[i].min_uV || v > regulators[i].max_uV) { - r = regulator_set_voltage(reg, regulators[i].min_uV, - regulators[i].max_uV); - if (r) { - dev_err(&dssdev->dev, - "failed to set regulator %s voltage\n", - regulators[i].name); - regulator_put(reg); - goto err; - } - } - - r = regulator_enable(reg); - if (r) { - dev_err(&dssdev->dev, "failed to enable regulator %s\n", - regulators[i].name); - regulator_put(reg); - goto err; - } - - regulators[i].regulator = reg; - } - - return 0; - -err: - free_regulators(regulators, i); - - return r; -} - /** * struct panel_config - panel configuration * @name: panel name @@ -150,8 +82,6 @@ struct panel_config { unsigned int low; } reset_sequence; - struct panel_regulator *regulators; - int num_regulators; }; enum { @@ -577,12 +507,6 @@ static const struct backlight_ops taal_bl_ops = { .update_status = taal_bl_update_status, }; -static void taal_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void taal_get_resolution(struct omap_dss_device *dssdev, u16 *xres, u16 *yres) { @@ -977,11 +901,6 @@ static int taal_probe(struct omap_dss_device *dssdev) atomic_set(&td->do_update, 0); - r = init_regulators(dssdev, panel_config->regulators, - panel_config->num_regulators); - if (r) - goto err_reg; - td->workqueue = create_singlethread_workqueue("taal_esd"); if (td->workqueue == NULL) { dev_err(&dssdev->dev, "can't create ESD workqueue\n"); @@ -1087,8 +1006,6 @@ err_bl: err_rst_gpio: destroy_workqueue(td->workqueue); err_wq: - free_regulators(panel_config->regulators, panel_config->num_regulators); -err_reg: kfree(td); err: return r; @@ -1125,9 +1042,6 @@ static void __exit taal_remove(struct omap_dss_device *dssdev) /* reset, to be sure that the panel is in a valid state */ taal_hw_reset(dssdev); - free_regulators(td->panel_config->regulators, - td->panel_config->num_regulators); - if (gpio_is_valid(panel_data->reset_gpio)) gpio_free(panel_data->reset_gpio); @@ -1909,8 +1823,6 @@ static struct omap_dss_driver taal_driver = { .run_test = taal_run_test, .memory_read = taal_memory_read, - .get_timings = taal_get_timings, - .driver = { .name = "taal", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c index 52637fa8fda8..bff306e041ca 100644 --- a/drivers/video/omap2/displays/panel-tfp410.c +++ b/drivers/video/omap2/displays/panel-tfp410.c @@ -47,13 +47,9 @@ struct panel_drv_data { struct mutex lock; int pd_gpio; -}; -static inline struct tfp410_platform_data -*get_pdata(const struct omap_dss_device *dssdev) -{ - return dssdev->data; -} + struct i2c_adapter *i2c_adapter; +}; static int tfp410_power_on(struct omap_dss_device *dssdev) { @@ -68,7 +64,7 @@ static int tfp410_power_on(struct omap_dss_device *dssdev) goto err0; if (gpio_is_valid(ddata->pd_gpio)) - gpio_set_value(ddata->pd_gpio, 1); + gpio_set_value_cansleep(ddata->pd_gpio, 1); return 0; err0: @@ -83,18 +79,18 @@ static void tfp410_power_off(struct omap_dss_device *dssdev) return; if (gpio_is_valid(ddata->pd_gpio)) - gpio_set_value(ddata->pd_gpio, 0); + gpio_set_value_cansleep(ddata->pd_gpio, 0); omapdss_dpi_display_disable(dssdev); } static int tfp410_probe(struct omap_dss_device *dssdev) { - struct tfp410_platform_data *pdata = get_pdata(dssdev); struct panel_drv_data *ddata; int r; + int i2c_bus_num; - ddata = kzalloc(sizeof(*ddata), GFP_KERNEL); + ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; @@ -104,10 +100,15 @@ static int tfp410_probe(struct omap_dss_device *dssdev) ddata->dssdev = dssdev; mutex_init(&ddata->lock); - if (pdata) + if (dssdev->data) { + struct tfp410_platform_data *pdata = dssdev->data; + ddata->pd_gpio = pdata->power_down_gpio; - else + i2c_bus_num = pdata->i2c_bus_num; + } else { ddata->pd_gpio = -1; + i2c_bus_num = -1; + } if (gpio_is_valid(ddata->pd_gpio)) { r = gpio_request_one(ddata->pd_gpio, GPIOF_OUT_INIT_LOW, @@ -115,13 +116,31 @@ static int tfp410_probe(struct omap_dss_device *dssdev) if (r) { dev_err(&dssdev->dev, "Failed to request PD GPIO %d\n", ddata->pd_gpio); - ddata->pd_gpio = -1; + return r; } } + if (i2c_bus_num != -1) { + struct i2c_adapter *adapter; + + adapter = i2c_get_adapter(i2c_bus_num); + if (!adapter) { + dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", + i2c_bus_num); + r = -EINVAL; + goto err_i2c; + } + + ddata->i2c_adapter = adapter; + } + dev_set_drvdata(&dssdev->dev, ddata); return 0; +err_i2c: + if (gpio_is_valid(ddata->pd_gpio)) + gpio_free(ddata->pd_gpio); + return r; } static void __exit tfp410_remove(struct omap_dss_device *dssdev) @@ -130,14 +149,15 @@ static void __exit tfp410_remove(struct omap_dss_device *dssdev) mutex_lock(&ddata->lock); + if (ddata->i2c_adapter) + i2c_put_adapter(ddata->i2c_adapter); + if (gpio_is_valid(ddata->pd_gpio)) gpio_free(ddata->pd_gpio); dev_set_drvdata(&dssdev->dev, NULL); mutex_unlock(&ddata->lock); - - kfree(ddata); } static int tfp410_enable(struct omap_dss_device *dssdev) @@ -269,27 +289,17 @@ static int tfp410_read_edid(struct omap_dss_device *dssdev, u8 *edid, int len) { struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); - struct tfp410_platform_data *pdata = get_pdata(dssdev); - struct i2c_adapter *adapter; int r, l, bytes_read; mutex_lock(&ddata->lock); - if (pdata->i2c_bus_num == 0) { + if (!ddata->i2c_adapter) { r = -ENODEV; goto err; } - adapter = i2c_get_adapter(pdata->i2c_bus_num); - if (!adapter) { - dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", - pdata->i2c_bus_num); - r = -EINVAL; - goto err; - } - l = min(EDID_LENGTH, len); - r = tfp410_ddc_read(adapter, edid, l, 0); + r = tfp410_ddc_read(ddata->i2c_adapter, edid, l, 0); if (r) goto err; @@ -299,7 +309,7 @@ static int tfp410_read_edid(struct omap_dss_device *dssdev, if (len > EDID_LENGTH && edid[0x7e] > 0) { l = min(EDID_LENGTH, len - EDID_LENGTH); - r = tfp410_ddc_read(adapter, edid + EDID_LENGTH, + r = tfp410_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH, l, EDID_LENGTH); if (r) goto err; @@ -319,21 +329,15 @@ err: static bool tfp410_detect(struct omap_dss_device *dssdev) { struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); - struct tfp410_platform_data *pdata = get_pdata(dssdev); - struct i2c_adapter *adapter; unsigned char out; int r; mutex_lock(&ddata->lock); - if (pdata->i2c_bus_num == 0) - goto out; - - adapter = i2c_get_adapter(pdata->i2c_bus_num); - if (!adapter) + if (!ddata->i2c_adapter) goto out; - r = tfp410_ddc_read(adapter, &out, 1, 0); + r = tfp410_ddc_read(ddata->i2c_adapter, &out, 1, 0); mutex_unlock(&ddata->lock); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index 32f3fcd7f0f0..4b6448b3c31f 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -272,13 +272,16 @@ static const struct omap_video_timings tpo_td043_timings = { static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043) { int nreset_gpio = tpo_td043->nreset_gpio; + int r; if (tpo_td043->powered_on) return 0; - regulator_enable(tpo_td043->vcc_reg); + r = regulator_enable(tpo_td043->vcc_reg); + if (r != 0) + return r; - /* wait for regulator to stabilize */ + /* wait for panel to stabilize */ msleep(160); if (gpio_is_valid(nreset_gpio)) @@ -470,6 +473,18 @@ static void tpo_td043_remove(struct omap_dss_device *dssdev) gpio_free(nreset_gpio); } +static void tpo_td043_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + dpi_set_timings(dssdev, timings); +} + +static int tpo_td043_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + return dpi_check_timings(dssdev, timings); +} + static struct omap_dss_driver tpo_td043_driver = { .probe = tpo_td043_probe, .remove = tpo_td043_remove, @@ -481,6 +496,9 @@ static struct omap_dss_driver tpo_td043_driver = { .set_mirror = tpo_td043_set_hmirror, .get_mirror = tpo_td043_get_hmirror, + .set_timings = tpo_td043_set_timings, + .check_timings = tpo_td043_check_timings, + .driver = { .name = "tpo_td043mtea1_panel", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 7be7c06a249e..43324e5ed25f 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -68,6 +68,10 @@ config OMAP4_DSS_HDMI HDMI Interface. This adds the High Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI specification. +config OMAP4_DSS_HDMI_AUDIO + bool + depends on OMAP4_DSS_HDMI + config OMAP2_DSS_SDI bool "SDI support" depends on ARCH_OMAP3 @@ -90,15 +94,6 @@ config OMAP2_DSS_DSI See http://www.mipi.org/ for DSI spesifications. -config OMAP2_DSS_FAKE_VSYNC - bool "Fake VSYNC irq from manual update displays" - default n - help - If this is selected, DSI will generate a fake DISPC VSYNC interrupt - when DSI has sent a frame. This is only needed with DSI or RFBI - displays using manual mode, and you want VSYNC to, for example, - time animation. - config OMAP2_DSS_MIN_FCK_PER_PCK int "Minimum FCK/PCK ratio (for scaling)" range 0 32 diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c index b10b3bc1931e..ab22cc224f3e 100644 --- a/drivers/video/omap2/dss/apply.c +++ b/drivers/video/omap2/dss/apply.c @@ -99,6 +99,11 @@ struct mgr_priv_data { /* If true, a display is enabled using this manager */ bool enabled; + + bool extra_info_dirty; + bool shadow_extra_info_dirty; + + struct omap_video_timings timings; }; static struct { @@ -176,7 +181,7 @@ static bool mgr_manual_update(struct omap_overlay_manager *mgr) } static int dss_check_settings_low(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, bool applying) + bool applying) { struct omap_overlay_info *oi; struct omap_overlay_manager_info *mi; @@ -187,6 +192,9 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr, mp = get_mgr_priv(mgr); + if (!mp->enabled) + return 0; + if (applying && mp->user_info_dirty) mi = &mp->user_info; else @@ -206,26 +214,24 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr, ois[ovl->id] = oi; } - return dss_mgr_check(mgr, dssdev, mi, ois); + return dss_mgr_check(mgr, mi, &mp->timings, ois); } /* * check manager and overlay settings using overlay_info from data->info */ -static int dss_check_settings(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev) +static int dss_check_settings(struct omap_overlay_manager *mgr) { - return dss_check_settings_low(mgr, dssdev, false); + return dss_check_settings_low(mgr, false); } /* * check manager and overlay settings using overlay_info from ovl->info if * dirty and from data->info otherwise */ -static int dss_check_settings_apply(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev) +static int dss_check_settings_apply(struct omap_overlay_manager *mgr) { - return dss_check_settings_low(mgr, dssdev, true); + return dss_check_settings_low(mgr, true); } static bool need_isr(void) @@ -261,6 +267,20 @@ static bool need_isr(void) if (mp->shadow_info_dirty) return true; + /* + * NOTE: we don't check extra_info flags for disabled + * managers, once the manager is enabled, the extra_info + * related manager changes will be taken in by HW. + */ + + /* to write new values to registers */ + if (mp->extra_info_dirty) + return true; + + /* to set GO bit */ + if (mp->shadow_extra_info_dirty) + return true; + list_for_each_entry(ovl, &mgr->overlays, list) { struct ovl_priv_data *op; @@ -305,7 +325,7 @@ static bool need_go(struct omap_overlay_manager *mgr) mp = get_mgr_priv(mgr); - if (mp->shadow_info_dirty) + if (mp->shadow_info_dirty || mp->shadow_extra_info_dirty) return true; list_for_each_entry(ovl, &mgr->overlays, list) { @@ -320,20 +340,16 @@ static bool need_go(struct omap_overlay_manager *mgr) /* returns true if an extra_info field is currently being updated */ static bool extra_info_update_ongoing(void) { - const int num_ovls = omap_dss_get_num_overlays(); - struct ovl_priv_data *op; - struct omap_overlay *ovl; - struct mgr_priv_data *mp; + const int num_mgrs = dss_feat_get_num_mgrs(); int i; - for (i = 0; i < num_ovls; ++i) { - ovl = omap_dss_get_overlay(i); - op = get_ovl_priv(ovl); - - if (!ovl->manager) - continue; + for (i = 0; i < num_mgrs; ++i) { + struct omap_overlay_manager *mgr; + struct omap_overlay *ovl; + struct mgr_priv_data *mp; - mp = get_mgr_priv(ovl->manager); + mgr = omap_dss_get_overlay_manager(i); + mp = get_mgr_priv(mgr); if (!mp->enabled) continue; @@ -341,8 +357,15 @@ static bool extra_info_update_ongoing(void) if (!mp->updating) continue; - if (op->extra_info_dirty || op->shadow_extra_info_dirty) + if (mp->extra_info_dirty || mp->shadow_extra_info_dirty) return true; + + list_for_each_entry(ovl, &mgr->overlays, list) { + struct ovl_priv_data *op = get_ovl_priv(ovl); + + if (op->extra_info_dirty || op->shadow_extra_info_dirty) + return true; + } } return false; @@ -525,11 +548,13 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl) oi = &op->info; + mp = get_mgr_priv(ovl->manager); + replication = dss_use_replication(ovl->manager->device, oi->color_mode); ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC; - r = dispc_ovl_setup(ovl->id, oi, ilace, replication); + r = dispc_ovl_setup(ovl->id, oi, ilace, replication, &mp->timings); if (r) { /* * We can't do much here, as this function can be called from @@ -543,8 +568,6 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl) return; } - mp = get_mgr_priv(ovl->manager); - op->info_dirty = false; if (mp->updating) op->shadow_info_dirty = true; @@ -601,6 +624,22 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr) } } +static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr) +{ + struct mgr_priv_data *mp = get_mgr_priv(mgr); + + DSSDBGF("%d", mgr->id); + + if (!mp->extra_info_dirty) + return; + + dispc_mgr_set_timings(mgr->id, &mp->timings); + + mp->extra_info_dirty = false; + if (mp->updating) + mp->shadow_extra_info_dirty = true; +} + static void dss_write_regs_common(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); @@ -646,7 +685,7 @@ static void dss_write_regs(void) if (!mp->enabled || mgr_manual_update(mgr) || mp->busy) continue; - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("cannot write registers for manager %s: " "illegal configuration\n", mgr->name); @@ -654,6 +693,7 @@ static void dss_write_regs(void) } dss_mgr_write_regs(mgr); + dss_mgr_write_regs_extra(mgr); } } @@ -693,6 +733,7 @@ static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) mp = get_mgr_priv(mgr); mp->shadow_info_dirty = false; + mp->shadow_extra_info_dirty = false; list_for_each_entry(ovl, &mgr->overlays, list) { op = get_ovl_priv(ovl); @@ -711,7 +752,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) WARN_ON(mp->updating); - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("cannot start manual update: illegal configuration\n"); spin_unlock_irqrestore(&data_lock, flags); @@ -719,6 +760,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) } dss_mgr_write_regs(mgr); + dss_mgr_write_regs_extra(mgr); dss_write_regs_common(); @@ -857,7 +899,7 @@ int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) spin_lock_irqsave(&data_lock, flags); - r = dss_check_settings_apply(mgr, mgr->device); + r = dss_check_settings_apply(mgr); if (r) { spin_unlock_irqrestore(&data_lock, flags); DSSERR("failed to apply settings: illegal configuration.\n"); @@ -918,16 +960,13 @@ static void dss_ovl_setup_fifo(struct omap_overlay *ovl, bool use_fifo_merge) { struct ovl_priv_data *op = get_ovl_priv(ovl); - struct omap_dss_device *dssdev; u32 fifo_low, fifo_high; if (!op->enabled && !op->enabling) return; - dssdev = ovl->manager->device; - dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high, - use_fifo_merge); + use_fifo_merge, ovl_manual_update(ovl)); dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high); } @@ -1050,7 +1089,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr) mp->enabled = true; - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("failed to enable manager %d: check_settings failed\n", mgr->id); @@ -1225,6 +1264,35 @@ err: return r; } +static void dss_apply_mgr_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings) +{ + struct mgr_priv_data *mp = get_mgr_priv(mgr); + + mp->timings = *timings; + mp->extra_info_dirty = true; +} + +void dss_mgr_set_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings) +{ + unsigned long flags; + + mutex_lock(&apply_lock); + + spin_lock_irqsave(&data_lock, flags); + + dss_apply_mgr_timings(mgr, timings); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + wait_pending_extra_info_updates(); + + mutex_unlock(&apply_lock); +} int dss_ovl_set_info(struct omap_overlay *ovl, struct omap_overlay_info *info) @@ -1393,7 +1461,7 @@ int dss_ovl_enable(struct omap_overlay *ovl) op->enabling = true; - r = dss_check_settings(ovl->manager, ovl->manager->device); + r = dss_check_settings(ovl->manager); if (r) { DSSERR("failed to enable overlay %d: check_settings failed\n", ovl->id); diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index e8a120771ac6..72ded9cd2cb0 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -43,6 +43,8 @@ static struct { struct regulator *vdds_dsi_reg; struct regulator *vdds_sdi_reg; + + const char *default_display_name; } core; static char *def_disp_name; @@ -54,9 +56,6 @@ bool dss_debug; module_param_named(debug, dss_debug, bool, 0644); #endif -static int omap_dss_register_device(struct omap_dss_device *); -static void omap_dss_unregister_device(struct omap_dss_device *); - /* REGULATORS */ struct regulator *dss_get_vdds_dsi(void) @@ -87,6 +86,51 @@ struct regulator *dss_get_vdds_sdi(void) return reg; } +int dss_get_ctx_loss_count(struct device *dev) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + int cnt; + + if (!board_data->get_context_loss_count) + return -ENOENT; + + cnt = board_data->get_context_loss_count(dev); + + WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt); + + return cnt; +} + +int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + + if (!board_data->dsi_enable_pads) + return -ENOENT; + + return board_data->dsi_enable_pads(dsi_id, lane_mask); +} + +void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + + if (!board_data->dsi_enable_pads) + return; + + return board_data->dsi_disable_pads(dsi_id, lane_mask); +} + +int dss_set_min_bus_tput(struct device *dev, unsigned long tput) +{ + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + + if (pdata->set_min_bus_tput) + return pdata->set_min_bus_tput(dev, tput); + else + return 0; +} + #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) static int dss_debug_show(struct seq_file *s, void *unused) { @@ -121,34 +165,6 @@ static int dss_initialize_debugfs(void) debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, &dss_debug_dump_clocks, &dss_debug_fops); -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - debugfs_create_file("dispc_irq", S_IRUGO, dss_debugfs_dir, - &dispc_dump_irqs, &dss_debug_fops); -#endif - -#if defined(CONFIG_OMAP2_DSS_DSI) && defined(CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS) - dsi_create_debugfs_files_irq(dss_debugfs_dir, &dss_debug_fops); -#endif - - debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, - &dss_dump_regs, &dss_debug_fops); - debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir, - &dispc_dump_regs, &dss_debug_fops); -#ifdef CONFIG_OMAP2_DSS_RFBI - debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir, - &rfbi_dump_regs, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - dsi_create_debugfs_files_reg(dss_debugfs_dir, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, - &venc_dump_regs, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP4_DSS_HDMI - debugfs_create_file("hdmi", S_IRUGO, dss_debugfs_dir, - &hdmi_dump_regs, &dss_debug_fops); -#endif return 0; } @@ -157,6 +173,19 @@ static void dss_uninitialize_debugfs(void) if (dss_debugfs_dir) debugfs_remove_recursive(dss_debugfs_dir); } + +int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) +{ + struct dentry *d; + + d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, + write, &dss_debug_fops); + + if (IS_ERR(d)) + return PTR_ERR(d); + + return 0; +} #else /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ static inline int dss_initialize_debugfs(void) { @@ -165,14 +194,18 @@ static inline int dss_initialize_debugfs(void) static inline void dss_uninitialize_debugfs(void) { } +static inline int dss_debugfs_create_file(const char *name, + void (*write)(struct seq_file *)) +{ + return 0; +} #endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ /* PLATFORM DEVICE */ -static int omap_dss_probe(struct platform_device *pdev) +static int __init omap_dss_probe(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; int r; - int i; core.pdev = pdev; @@ -187,28 +220,13 @@ static int omap_dss_probe(struct platform_device *pdev) if (r) goto err_debugfs; - for (i = 0; i < pdata->num_devices; ++i) { - struct omap_dss_device *dssdev = pdata->devices[i]; - - r = omap_dss_register_device(dssdev); - if (r) { - DSSERR("device %d %s register failed %d\n", i, - dssdev->name ?: "unnamed", r); - - while (--i >= 0) - omap_dss_unregister_device(pdata->devices[i]); - - goto err_register; - } - - if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0) - pdata->default_device = dssdev; - } + if (def_disp_name) + core.default_display_name = def_disp_name; + else if (pdata->default_device) + core.default_display_name = pdata->default_device->name; return 0; -err_register: - dss_uninitialize_debugfs(); err_debugfs: return r; @@ -216,17 +234,11 @@ err_debugfs: static int omap_dss_remove(struct platform_device *pdev) { - struct omap_dss_board_info *pdata = pdev->dev.platform_data; - int i; - dss_uninitialize_debugfs(); dss_uninit_overlays(pdev); dss_uninit_overlay_managers(pdev); - for (i = 0; i < pdata->num_devices; ++i) - omap_dss_unregister_device(pdata->devices[i]); - return 0; } @@ -251,7 +263,6 @@ static int omap_dss_resume(struct platform_device *pdev) } static struct platform_driver omap_dss_driver = { - .probe = omap_dss_probe, .remove = omap_dss_remove, .shutdown = omap_dss_shutdown, .suspend = omap_dss_suspend, @@ -326,7 +337,6 @@ static int dss_driver_probe(struct device *dev) int r; struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); struct omap_dss_device *dssdev = to_dss_device(dev); - struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; bool force; DSSDBG("driver_probe: dev %s/%s, drv %s\n", @@ -335,7 +345,8 @@ static int dss_driver_probe(struct device *dev) dss_init_device(core.pdev, dssdev); - force = pdata->default_device == dssdev; + force = core.default_display_name && + strcmp(core.default_display_name, dssdev->name) == 0; dss_recheck_connections(dssdev, force); r = dssdrv->probe(dssdev); @@ -381,6 +392,8 @@ int omap_dss_register_driver(struct omap_dss_driver *dssdriver) if (dssdriver->get_recommended_bpp == NULL) dssdriver->get_recommended_bpp = omapdss_default_get_recommended_bpp; + if (dssdriver->get_timings == NULL) + dssdriver->get_timings = omapdss_default_get_timings; return driver_register(&dssdriver->driver); } @@ -427,27 +440,38 @@ static void omap_dss_dev_release(struct device *dev) reset_device(dev, 0); } -static int omap_dss_register_device(struct omap_dss_device *dssdev) +int omap_dss_register_device(struct omap_dss_device *dssdev, + struct device *parent, int disp_num) { - static int dev_num; - WARN_ON(!dssdev->driver_name); reset_device(&dssdev->dev, 1); dssdev->dev.bus = &dss_bus_type; - dssdev->dev.parent = &dss_bus; + dssdev->dev.parent = parent; dssdev->dev.release = omap_dss_dev_release; - dev_set_name(&dssdev->dev, "display%d", dev_num++); + dev_set_name(&dssdev->dev, "display%d", disp_num); return device_register(&dssdev->dev); } -static void omap_dss_unregister_device(struct omap_dss_device *dssdev) +void omap_dss_unregister_device(struct omap_dss_device *dssdev) { device_unregister(&dssdev->dev); } +static int dss_unregister_dss_dev(struct device *dev, void *data) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + omap_dss_unregister_device(dssdev); + return 0; +} + +void omap_dss_unregister_child_devices(struct device *parent) +{ + device_for_each_child(parent, NULL, dss_unregister_dss_dev); +} + /* BUS */ -static int omap_dss_bus_register(void) +static int __init omap_dss_bus_register(void) { int r; @@ -469,12 +493,56 @@ static int omap_dss_bus_register(void) } /* INIT */ +static int (*dss_output_drv_reg_funcs[])(void) __initdata = { +#ifdef CONFIG_OMAP2_DSS_DPI + dpi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + venc_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI + hdmi_init_platform_driver, +#endif +}; + +static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = { +#ifdef CONFIG_OMAP2_DSS_DPI + dpi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + venc_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI + hdmi_uninit_platform_driver, +#endif +}; + +static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)]; static int __init omap_dss_register_drivers(void) { int r; + int i; - r = platform_driver_register(&omap_dss_driver); + r = platform_driver_probe(&omap_dss_driver, omap_dss_probe); if (r) return r; @@ -490,40 +558,18 @@ static int __init omap_dss_register_drivers(void) goto err_dispc; } - r = rfbi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize rfbi platform driver\n"); - goto err_rfbi; - } - - r = venc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize venc platform driver\n"); - goto err_venc; - } - - r = dsi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSI platform driver\n"); - goto err_dsi; - } - - r = hdmi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize hdmi\n"); - goto err_hdmi; + /* + * It's ok if the output-driver register fails. It happens, for example, + * when there is no output-device (e.g. SDI for OMAP4). + */ + for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) { + r = dss_output_drv_reg_funcs[i](); + if (r == 0) + dss_output_drv_loaded[i] = true; } return 0; -err_hdmi: - dsi_uninit_platform_driver(); -err_dsi: - venc_uninit_platform_driver(); -err_venc: - rfbi_uninit_platform_driver(); -err_rfbi: - dispc_uninit_platform_driver(); err_dispc: dss_uninit_platform_driver(); err_dss: @@ -534,10 +580,13 @@ err_dss: static void __exit omap_dss_unregister_drivers(void) { - hdmi_uninit_platform_driver(); - dsi_uninit_platform_driver(); - venc_uninit_platform_driver(); - rfbi_uninit_platform_driver(); + int i; + + for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) { + if (dss_output_drv_loaded[i]) + dss_output_drv_unreg_funcs[i](); + } + dispc_uninit_platform_driver(); dss_uninit_platform_driver(); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index ee30937482e1..4749ac356469 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -131,23 +131,6 @@ static inline u32 dispc_read_reg(const u16 idx) return __raw_readl(dispc.base + idx); } -static int dispc_get_ctx_loss_count(void) -{ - struct device *dev = &dispc.pdev->dev; - struct omap_display_platform_data *pdata = dev->platform_data; - struct omap_dss_board_info *board_data = pdata->board_data; - int cnt; - - if (!board_data->get_context_loss_count) - return -ENOENT; - - cnt = board_data->get_context_loss_count(dev); - - WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt); - - return cnt; -} - #define SR(reg) \ dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg) #define RR(reg) \ @@ -251,7 +234,7 @@ static void dispc_save_context(void) if (dss_has_feature(FEAT_CORE_CLK_DIV)) SR(DIVISOR); - dispc.ctx_loss_cnt = dispc_get_ctx_loss_count(); + dispc.ctx_loss_cnt = dss_get_ctx_loss_count(&dispc.pdev->dev); dispc.ctx_valid = true; DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt); @@ -266,7 +249,7 @@ static void dispc_restore_context(void) if (!dispc.ctx_valid) return; - ctx = dispc_get_ctx_loss_count(); + ctx = dss_get_ctx_loss_count(&dispc.pdev->dev); if (ctx >= 0 && ctx == dispc.ctx_loss_cnt) return; @@ -413,14 +396,6 @@ static inline bool dispc_mgr_is_lcd(enum omap_channel channel) return false; } -static struct omap_dss_device *dispc_mgr_get_device(enum omap_channel channel) -{ - struct omap_overlay_manager *mgr = - omap_dss_get_overlay_manager(channel); - - return mgr ? mgr->device : NULL; -} - u32 dispc_mgr_get_vsync_irq(enum omap_channel channel) { switch (channel) { @@ -432,6 +407,7 @@ u32 dispc_mgr_get_vsync_irq(enum omap_channel channel) return DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; default: BUG(); + return 0; } } @@ -446,6 +422,7 @@ u32 dispc_mgr_get_framedone_irq(enum omap_channel channel) return 0; default: BUG(); + return 0; } } @@ -764,7 +741,7 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, case OMAP_DSS_COLOR_XRGB16_1555: m = 0xf; break; default: - BUG(); break; + BUG(); return; } } else { switch (color_mode) { @@ -801,13 +778,25 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, case OMAP_DSS_COLOR_XRGB16_1555: m = 0xf; break; default: - BUG(); break; + BUG(); return; } } REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); } +static void dispc_ovl_configure_burst_type(enum omap_plane plane, + enum omap_dss_rotation_type rotation_type) +{ + if (dss_has_feature(FEAT_BURST_2D) == 0) + return; + + if (rotation_type == OMAP_DSS_ROT_TILER) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29); + else + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29); +} + void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) { int shift; @@ -845,6 +834,7 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) break; default: BUG(); + return; } val = FLD_MOD(val, chan, shift, shift); @@ -872,6 +862,7 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane) break; default: BUG(); + return 0; } val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); @@ -983,20 +974,13 @@ static void dispc_ovl_enable_replication(enum omap_plane plane, bool enable) REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift); } -void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height) +static void dispc_mgr_set_size(enum omap_channel channel, u16 width, + u16 height) { u32 val; - BUG_ON((width > (1 << 11)) || (height > (1 << 11))); - val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(DISPC_SIZE_MGR(channel), val); -} -void dispc_set_digit_size(u16 width, u16 height) -{ - u32 val; - BUG_ON((width > (1 << 11)) || (height > (1 << 11))); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT), val); + dispc_write_reg(DISPC_SIZE_MGR(channel), val); } static void dispc_read_plane_fifo_sizes(void) @@ -1063,7 +1047,8 @@ void dispc_enable_fifomerge(bool enable) } void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, - u32 *fifo_low, u32 *fifo_high, bool use_fifomerge) + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, + bool manual_update) { /* * All sizes are in bytes. Both the buffer and burst are made of @@ -1091,7 +1076,7 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, * combined fifo size */ - if (dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { + if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { *fifo_low = ovl_fifo_size - burst_size * 2; *fifo_high = total_fifo_size - burst_size; } else { @@ -1185,6 +1170,94 @@ static void dispc_ovl_set_scale_param(enum omap_plane plane, dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp); } +static void dispc_ovl_set_accu_uv(enum omap_plane plane, + u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, + bool ilace, enum omap_color_mode color_mode, u8 rotation) +{ + int h_accu2_0, h_accu2_1; + int v_accu2_0, v_accu2_1; + int chroma_hinc, chroma_vinc; + int idx; + + struct accu { + s8 h0_m, h0_n; + s8 h1_m, h1_n; + s8 v0_m, v0_n; + s8 v1_m, v1_n; + }; + + const struct accu *accu_table; + const struct accu *accu_val; + + static const struct accu accu_nv12[4] = { + { 0, 1, 0, 1 , -1, 2, 0, 1 }, + { 1, 2, -3, 4 , 0, 1, 0, 1 }, + { -1, 1, 0, 1 , -1, 2, 0, 1 }, + { -1, 2, -1, 2 , -1, 1, 0, 1 }, + }; + + static const struct accu accu_nv12_ilace[4] = { + { 0, 1, 0, 1 , -3, 4, -1, 4 }, + { -1, 4, -3, 4 , 0, 1, 0, 1 }, + { -1, 1, 0, 1 , -1, 4, -3, 4 }, + { -3, 4, -3, 4 , -1, 1, 0, 1 }, + }; + + static const struct accu accu_yuv[4] = { + { 0, 1, 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, 0, 1, 0, 1 }, + { -1, 1, 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, -1, 1, 0, 1 }, + }; + + switch (rotation) { + case OMAP_DSS_ROT_0: + idx = 0; + break; + case OMAP_DSS_ROT_90: + idx = 1; + break; + case OMAP_DSS_ROT_180: + idx = 2; + break; + case OMAP_DSS_ROT_270: + idx = 3; + break; + default: + BUG(); + return; + } + + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + if (ilace) + accu_table = accu_nv12_ilace; + else + accu_table = accu_nv12; + break; + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + accu_table = accu_yuv; + break; + default: + BUG(); + return; + } + + accu_val = &accu_table[idx]; + + chroma_hinc = 1024 * orig_width / out_width; + chroma_vinc = 1024 * orig_height / out_height; + + h_accu2_0 = (accu_val->h0_m * chroma_hinc / accu_val->h0_n) % 1024; + h_accu2_1 = (accu_val->h1_m * chroma_hinc / accu_val->h1_n) % 1024; + v_accu2_0 = (accu_val->v0_m * chroma_vinc / accu_val->v0_n) % 1024; + v_accu2_1 = (accu_val->v1_m * chroma_vinc / accu_val->v1_n) % 1024; + + dispc_ovl_set_vid_accu2_0(plane, h_accu2_0, v_accu2_0); + dispc_ovl_set_vid_accu2_1(plane, h_accu2_1, v_accu2_1); +} + static void dispc_ovl_set_scaling_common(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, @@ -1258,6 +1331,10 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); return; } + + dispc_ovl_set_accu_uv(plane, orig_width, orig_height, out_width, + out_height, ilace, color_mode, rotation); + switch (color_mode) { case OMAP_DSS_COLOR_NV12: /* UV is subsampled by 2 vertically*/ @@ -1280,6 +1357,7 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, break; default: BUG(); + return; } if (out_width != orig_width) @@ -1297,9 +1375,6 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); /* set V scaling */ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); - - dispc_ovl_set_vid_accu2_0(plane, 0x80, 0); - dispc_ovl_set_vid_accu2_1(plane, 0x80, 0); } static void dispc_ovl_set_scaling(enum omap_plane plane, @@ -1410,6 +1485,7 @@ static int color_mode_to_bpp(enum omap_color_mode color_mode) return 32; default: BUG(); + return 0; } } @@ -1423,6 +1499,7 @@ static s32 pixinc(int pixels, u8 ps) return 1 - (-pixels + 1) * ps; else BUG(); + return 0; } static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, @@ -1431,7 +1508,7 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, enum omap_color_mode color_mode, bool fieldmode, unsigned int field_offset, unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc) + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) { u8 ps; @@ -1477,10 +1554,10 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, else *offset0 = 0; - *row_inc = pixinc(1 + (screen_width - width) + - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 + + (y_predecim * screen_width - x_predecim * width) + + (fieldmode ? screen_width : 0), ps); + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_0 + 4: @@ -1498,14 +1575,15 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, *offset0 = field_offset * screen_width * ps; else *offset0 = 0; - *row_inc = pixinc(1 - (screen_width + width) - - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 - + (y_predecim * screen_width + x_predecim * width) - + (fieldmode ? screen_width : 0), ps); + *pix_inc = pixinc(x_predecim, ps); break; default: BUG(); + return; } } @@ -1515,7 +1593,7 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, enum omap_color_mode color_mode, bool fieldmode, unsigned int field_offset, unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc) + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) { u8 ps; u16 fbw, fbh; @@ -1557,10 +1635,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(1 + (screen_width - fbw) + - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 + + (y_predecim * screen_width - fbw * x_predecim) + + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_90: *offset1 = screen_width * (fbh - 1) * ps; @@ -1568,9 +1650,9 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * (fbh - 1) + 1 + - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(-screen_width, ps); + *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) + + y_predecim + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(-x_predecim * screen_width, ps); break; case OMAP_DSS_ROT_180: *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; @@ -1579,10 +1661,13 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, else *offset0 = *offset1; *row_inc = pixinc(-1 - - (screen_width - fbw) - - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(-1, ps); + (y_predecim * screen_width - fbw * x_predecim) - + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(-x_predecim, 2 * ps); + else + *pix_inc = pixinc(-x_predecim, ps); break; case OMAP_DSS_ROT_270: *offset1 = (fbw - 1) * ps; @@ -1590,9 +1675,9 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(-screen_width * (fbh - 1) - 1 - - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(screen_width, ps); + *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) - + y_predecim - (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(x_predecim * screen_width, ps); break; /* mirroring */ @@ -1602,10 +1687,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * 2 - 1 + + *row_inc = pixinc(y_predecim * screen_width * 2 - 1 + (fieldmode ? screen_width : 0), ps); - *pix_inc = pixinc(-1, ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(-x_predecim, 2 * ps); + else + *pix_inc = pixinc(-x_predecim, ps); break; case OMAP_DSS_ROT_90 + 4: @@ -1614,10 +1703,10 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(-screen_width * (fbh - 1) + 1 + - (fieldmode ? 1 : 0), + *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) + + y_predecim + (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(screen_width, ps); + *pix_inc = pixinc(x_predecim * screen_width, ps); break; case OMAP_DSS_ROT_180 + 4: @@ -1626,10 +1715,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(1 - screen_width * 2 - + *row_inc = pixinc(1 - y_predecim * screen_width * 2 - (fieldmode ? screen_width : 0), ps); - *pix_inc = pixinc(1, ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_270 + 4: @@ -1638,34 +1731,130 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * (fbh - 1) - 1 - - (fieldmode ? 1 : 0), + *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) - + y_predecim - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(-screen_width, ps); + *pix_inc = pixinc(-x_predecim * screen_width, ps); break; default: BUG(); + return; + } +} + +static void calc_tiler_rotation_offset(u16 screen_width, u16 width, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) +{ + u8 ps; + + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + case OMAP_DSS_COLOR_CLUT2: + case OMAP_DSS_COLOR_CLUT4: + case OMAP_DSS_COLOR_CLUT8: + BUG(); + return; + default: + ps = color_mode_to_bpp(color_mode) / 8; + break; } + + DSSDBG("scrw %d, width %d\n", screen_width, width); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) + + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); } -static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, +/* + * This function is used to avoid synclosts in OMAP3, because of some + * undocumented horizontal position and timing related limitations. + */ +static int check_horiz_timing_omap3(enum omap_channel channel, + const struct omap_video_timings *t, u16 pos_x, + u16 width, u16 height, u16 out_width, u16 out_height) +{ + int DS = DIV_ROUND_UP(height, out_height); + unsigned long nonactive, lclk, pclk; + static const u8 limits[3] = { 8, 10, 20 }; + u64 val, blank; + int i; + + nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width; + pclk = dispc_mgr_pclk_rate(channel); + if (dispc_mgr_is_lcd(channel)) + lclk = dispc_mgr_lclk_rate(channel); + else + lclk = dispc_fclk_rate(); + + i = 0; + if (out_height < height) + i++; + if (out_width < width) + i++; + blank = div_u64((u64)(t->hbp + t->hsw + t->hfp) * lclk, pclk); + DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]); + if (blank <= limits[i]) + return -EINVAL; + + /* + * Pixel data should be prepared before visible display point starts. + * So, atleast DS-2 lines must have already been fetched by DISPC + * during nonactive - pos_x period. + */ + val = div_u64((u64)(nonactive - pos_x) * lclk, pclk); + DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n", + val, max(0, DS - 2) * width); + if (val < max(0, DS - 2) * width) + return -EINVAL; + + /* + * All lines need to be refilled during the nonactive period of which + * only one line can be loaded during the active period. So, atleast + * DS - 1 lines should be loaded during nonactive period. + */ + val = div_u64((u64)nonactive * lclk, pclk); + DSSDBG("nonactive * pcd = %llu, max(0, DS - 1) * width = %d\n", + val, max(0, DS - 1) * width); + if (val < max(0, DS - 1) * width) + return -EINVAL; + + return 0; +} + +static unsigned long calc_core_clk_five_taps(enum omap_channel channel, + const struct omap_video_timings *mgr_timings, u16 width, u16 height, u16 out_width, u16 out_height, enum omap_color_mode color_mode) { - u32 fclk = 0; + u32 core_clk = 0; u64 tmp, pclk = dispc_mgr_pclk_rate(channel); if (height <= out_height && width <= out_width) return (unsigned long) pclk; if (height > out_height) { - struct omap_dss_device *dssdev = dispc_mgr_get_device(channel); - unsigned int ppl = dssdev->panel.timings.x_res; + unsigned int ppl = mgr_timings->x_res; tmp = pclk * height * out_width; do_div(tmp, 2 * out_height * ppl); - fclk = tmp; + core_clk = tmp; if (height > 2 * out_height) { if (ppl == out_width) @@ -1673,23 +1862,23 @@ static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, tmp = pclk * (height - 2 * out_height) * out_width; do_div(tmp, 2 * out_height * (ppl - out_width)); - fclk = max(fclk, (u32) tmp); + core_clk = max_t(u32, core_clk, tmp); } } if (width > out_width) { tmp = pclk * width; do_div(tmp, out_width); - fclk = max(fclk, (u32) tmp); + core_clk = max_t(u32, core_clk, tmp); if (color_mode == OMAP_DSS_COLOR_RGB24U) - fclk <<= 1; + core_clk <<= 1; } - return fclk; + return core_clk; } -static unsigned long calc_fclk(enum omap_channel channel, u16 width, +static unsigned long calc_core_clk(enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height) { unsigned int hf, vf; @@ -1730,15 +1919,20 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, } static int dispc_ovl_calc_scaling(enum omap_plane plane, - enum omap_channel channel, u16 width, u16 height, - u16 out_width, u16 out_height, - enum omap_color_mode color_mode, bool *five_taps) + enum omap_channel channel, + const struct omap_video_timings *mgr_timings, + u16 width, u16 height, u16 out_width, u16 out_height, + enum omap_color_mode color_mode, bool *five_taps, + int *x_predecim, int *y_predecim, u16 pos_x) { struct omap_overlay *ovl = omap_dss_get_overlay(plane); const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); const int maxsinglelinewidth = dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); - unsigned long fclk = 0; + const int max_decim_limit = 16; + unsigned long core_clk = 0; + int decim_x, decim_y, error, min_factor; + u16 in_width, in_height, in_width_max = 0; if (width == out_width && height == out_height) return 0; @@ -1746,64 +1940,154 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) return -EINVAL; - if (out_width < width / maxdownscale || - out_width > width * 8) + *x_predecim = max_decim_limit; + *y_predecim = max_decim_limit; + + if (color_mode == OMAP_DSS_COLOR_CLUT1 || + color_mode == OMAP_DSS_COLOR_CLUT2 || + color_mode == OMAP_DSS_COLOR_CLUT4 || + color_mode == OMAP_DSS_COLOR_CLUT8) { + *x_predecim = 1; + *y_predecim = 1; + *five_taps = false; + return 0; + } + + decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale); + decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale); + + min_factor = min(decim_x, decim_y); + + if (decim_x > *x_predecim || out_width > width * 8) return -EINVAL; - if (out_height < height / maxdownscale || - out_height > height * 8) + if (decim_y > *y_predecim || out_height > height * 8) return -EINVAL; if (cpu_is_omap24xx()) { - if (width > maxsinglelinewidth) - DSSERR("Cannot scale max input width exceeded"); *five_taps = false; - fclk = calc_fclk(channel, width, height, out_width, - out_height); + + do { + in_height = DIV_ROUND_UP(height, decim_y); + in_width = DIV_ROUND_UP(width, decim_x); + core_clk = calc_core_clk(channel, in_width, in_height, + out_width, out_height); + error = (in_width > maxsinglelinewidth || !core_clk || + core_clk > dispc_core_clk_rate()); + if (error) { + if (decim_x == decim_y) { + decim_x = min_factor; + decim_y++; + } else { + swap(decim_x, decim_y); + if (decim_x < decim_y) + decim_x++; + } + } + } while (decim_x <= *x_predecim && decim_y <= *y_predecim && + error); + + if (in_width > maxsinglelinewidth) { + DSSERR("Cannot scale max input width exceeded"); + return -EINVAL; + } } else if (cpu_is_omap34xx()) { - if (width > (maxsinglelinewidth * 2)) { + + do { + in_height = DIV_ROUND_UP(height, decim_y); + in_width = DIV_ROUND_UP(width, decim_x); + core_clk = calc_core_clk_five_taps(channel, mgr_timings, + in_width, in_height, out_width, out_height, + color_mode); + + error = check_horiz_timing_omap3(channel, mgr_timings, + pos_x, in_width, in_height, out_width, + out_height); + + if (in_width > maxsinglelinewidth) + if (in_height > out_height && + in_height < out_height * 2) + *five_taps = false; + if (!*five_taps) + core_clk = calc_core_clk(channel, in_width, + in_height, out_width, out_height); + error = (error || in_width > maxsinglelinewidth * 2 || + (in_width > maxsinglelinewidth && *five_taps) || + !core_clk || core_clk > dispc_core_clk_rate()); + if (error) { + if (decim_x == decim_y) { + decim_x = min_factor; + decim_y++; + } else { + swap(decim_x, decim_y); + if (decim_x < decim_y) + decim_x++; + } + } + } while (decim_x <= *x_predecim && decim_y <= *y_predecim + && error); + + if (check_horiz_timing_omap3(channel, mgr_timings, pos_x, width, + height, out_width, out_height)){ + DSSERR("horizontal timing too tight\n"); + return -EINVAL; + } + + if (in_width > (maxsinglelinewidth * 2)) { DSSERR("Cannot setup scaling"); DSSERR("width exceeds maximum width possible"); return -EINVAL; } - fclk = calc_fclk_five_taps(channel, width, height, out_width, - out_height, color_mode); - if (width > maxsinglelinewidth) { - if (height > out_height && height < out_height * 2) - *five_taps = false; - else { - DSSERR("cannot setup scaling with five taps"); - return -EINVAL; - } + + if (in_width > maxsinglelinewidth && *five_taps) { + DSSERR("cannot setup scaling with five taps"); + return -EINVAL; } - if (!*five_taps) - fclk = calc_fclk(channel, width, height, out_width, - out_height); } else { - if (width > maxsinglelinewidth) { + int decim_x_min = decim_x; + in_height = DIV_ROUND_UP(height, decim_y); + in_width_max = dispc_core_clk_rate() / + DIV_ROUND_UP(dispc_mgr_pclk_rate(channel), + out_width); + decim_x = DIV_ROUND_UP(width, in_width_max); + + decim_x = decim_x > decim_x_min ? decim_x : decim_x_min; + if (decim_x > *x_predecim) + return -EINVAL; + + do { + in_width = DIV_ROUND_UP(width, decim_x); + } while (decim_x <= *x_predecim && + in_width > maxsinglelinewidth && decim_x++); + + if (in_width > maxsinglelinewidth) { DSSERR("Cannot scale width exceeds max line width"); return -EINVAL; } - fclk = calc_fclk(channel, width, height, out_width, - out_height); + + core_clk = calc_core_clk(channel, in_width, in_height, + out_width, out_height); } - DSSDBG("required fclk rate = %lu Hz\n", fclk); - DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); + DSSDBG("required core clk rate = %lu Hz\n", core_clk); + DSSDBG("current core clk rate = %lu Hz\n", dispc_core_clk_rate()); - if (!fclk || fclk > dispc_fclk_rate()) { + if (!core_clk || core_clk > dispc_core_clk_rate()) { DSSERR("failed to set up scaling, " - "required fclk rate = %lu Hz, " - "current fclk rate = %lu Hz\n", - fclk, dispc_fclk_rate()); + "required core clk rate = %lu Hz, " + "current core clk rate = %lu Hz\n", + core_clk, dispc_core_clk_rate()); return -EINVAL; } + *x_predecim = decim_x; + *y_predecim = decim_y; return 0; } int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, - bool ilace, bool replication) + bool ilace, bool replication, + const struct omap_video_timings *mgr_timings) { struct omap_overlay *ovl = omap_dss_get_overlay(plane); bool five_taps = true; @@ -1814,8 +2098,11 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, s32 pix_inc; u16 frame_height = oi->height; unsigned int field_offset = 0; - u16 outw, outh; + u16 in_height = oi->height; + u16 in_width = oi->width; + u16 out_width, out_height; enum omap_channel channel; + int x_predecim = 1, y_predecim = 1; channel = dispc_ovl_get_channel_out(plane); @@ -1829,32 +2116,35 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, if (oi->paddr == 0) return -EINVAL; - outw = oi->out_width == 0 ? oi->width : oi->out_width; - outh = oi->out_height == 0 ? oi->height : oi->out_height; + out_width = oi->out_width == 0 ? oi->width : oi->out_width; + out_height = oi->out_height == 0 ? oi->height : oi->out_height; - if (ilace && oi->height == outh) + if (ilace && oi->height == out_height) fieldmode = 1; if (ilace) { if (fieldmode) - oi->height /= 2; + in_height /= 2; oi->pos_y /= 2; - outh /= 2; + out_height /= 2; DSSDBG("adjusting for ilace: height %d, pos_y %d, " "out_height %d\n", - oi->height, oi->pos_y, outh); + in_height, oi->pos_y, out_height); } if (!dss_feat_color_mode_supported(plane, oi->color_mode)) return -EINVAL; - r = dispc_ovl_calc_scaling(plane, channel, oi->width, oi->height, - outw, outh, oi->color_mode, - &five_taps); + r = dispc_ovl_calc_scaling(plane, channel, mgr_timings, in_width, + in_height, out_width, out_height, oi->color_mode, + &five_taps, &x_predecim, &y_predecim, oi->pos_x); if (r) return r; + in_width = DIV_ROUND_UP(in_width, x_predecim); + in_height = DIV_ROUND_UP(in_height, y_predecim); + if (oi->color_mode == OMAP_DSS_COLOR_YUV2 || oi->color_mode == OMAP_DSS_COLOR_UYVY || oi->color_mode == OMAP_DSS_COLOR_NV12) @@ -1868,32 +2158,46 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, * so the integer part must be added to the base address of the * bottom field. */ - if (!oi->height || oi->height == outh) + if (!in_height || in_height == out_height) field_offset = 0; else - field_offset = oi->height / outh / 2; + field_offset = in_height / out_height / 2; } /* Fields are independent but interleaved in memory. */ if (fieldmode) field_offset = 1; - if (oi->rotation_type == OMAP_DSS_ROT_DMA) + offset0 = 0; + offset1 = 0; + row_inc = 0; + pix_inc = 0; + + if (oi->rotation_type == OMAP_DSS_ROT_TILER) + calc_tiler_rotation_offset(oi->screen_width, in_width, + oi->color_mode, fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); + else if (oi->rotation_type == OMAP_DSS_ROT_DMA) calc_dma_rotation_offset(oi->rotation, oi->mirror, - oi->screen_width, oi->width, frame_height, + oi->screen_width, in_width, frame_height, oi->color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc); + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); else calc_vrfb_rotation_offset(oi->rotation, oi->mirror, - oi->screen_width, oi->width, frame_height, + oi->screen_width, in_width, frame_height, oi->color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc); + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", offset0, offset1, row_inc, pix_inc); dispc_ovl_set_color_mode(plane, oi->color_mode); + dispc_ovl_configure_burst_type(plane, oi->rotation_type); + dispc_ovl_set_ba0(plane, oi->paddr + offset0); dispc_ovl_set_ba1(plane, oi->paddr + offset1); @@ -1906,19 +2210,18 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, dispc_ovl_set_row_inc(plane, row_inc); dispc_ovl_set_pix_inc(plane, pix_inc); - DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, oi->width, - oi->height, outw, outh); + DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, in_width, + in_height, out_width, out_height); dispc_ovl_set_pos(plane, oi->pos_x, oi->pos_y); - dispc_ovl_set_pic_size(plane, oi->width, oi->height); + dispc_ovl_set_pic_size(plane, in_width, in_height); if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) { - dispc_ovl_set_scaling(plane, oi->width, oi->height, - outw, outh, - ilace, five_taps, fieldmode, + dispc_ovl_set_scaling(plane, in_width, in_height, out_width, + out_height, ilace, five_taps, fieldmode, oi->color_mode, oi->rotation); - dispc_ovl_set_vid_size(plane, outw, outh); + dispc_ovl_set_vid_size(plane, out_width, out_height); dispc_ovl_set_vid_color_conv(plane, cconv); } @@ -2087,8 +2390,10 @@ bool dispc_mgr_is_enabled(enum omap_channel channel) return !!REG_GET(DISPC_CONTROL, 1, 1); else if (channel == OMAP_DSS_CHANNEL_LCD2) return !!REG_GET(DISPC_CONTROL2, 0, 0); - else + else { BUG(); + return false; + } } void dispc_mgr_enable(enum omap_channel channel, bool enable) @@ -2285,6 +2590,12 @@ void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable) REG_FLD_MOD(DISPC_CONTROL, enable, 11, 11); } +static bool _dispc_mgr_size_ok(u16 width, u16 height) +{ + return width <= dss_feat_get_param_max(FEAT_PARAM_MGR_WIDTH) && + height <= dss_feat_get_param_max(FEAT_PARAM_MGR_HEIGHT); +} + static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, int vsw, int vfp, int vbp) { @@ -2309,11 +2620,20 @@ static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, return true; } -bool dispc_lcd_timings_ok(struct omap_video_timings *timings) +bool dispc_mgr_timings_ok(enum omap_channel channel, + const struct omap_video_timings *timings) { - return _dispc_lcd_timings_ok(timings->hsw, timings->hfp, - timings->hbp, timings->vsw, - timings->vfp, timings->vbp); + bool timings_ok; + + timings_ok = _dispc_mgr_size_ok(timings->x_res, timings->y_res); + + if (dispc_mgr_is_lcd(channel)) + timings_ok = timings_ok && _dispc_lcd_timings_ok(timings->hsw, + timings->hfp, timings->hbp, + timings->vsw, timings->vfp, + timings->vbp); + + return timings_ok; } static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, @@ -2340,37 +2660,45 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, } /* change name to mode? */ -void dispc_mgr_set_lcd_timings(enum omap_channel channel, +void dispc_mgr_set_timings(enum omap_channel channel, struct omap_video_timings *timings) { unsigned xtot, ytot; unsigned long ht, vt; + struct omap_video_timings t = *timings; + + DSSDBG("channel %d xres %u yres %u\n", channel, t.x_res, t.y_res); - if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp, - timings->hbp, timings->vsw, - timings->vfp, timings->vbp)) + if (!dispc_mgr_timings_ok(channel, &t)) { BUG(); + return; + } + + if (dispc_mgr_is_lcd(channel)) { + _dispc_mgr_set_lcd_timings(channel, t.hsw, t.hfp, t.hbp, t.vsw, + t.vfp, t.vbp); + + xtot = t.x_res + t.hfp + t.hsw + t.hbp; + ytot = t.y_res + t.vfp + t.vsw + t.vbp; - _dispc_mgr_set_lcd_timings(channel, timings->hsw, timings->hfp, - timings->hbp, timings->vsw, timings->vfp, - timings->vbp); + ht = (timings->pixel_clock * 1000) / xtot; + vt = (timings->pixel_clock * 1000) / xtot / ytot; - dispc_mgr_set_lcd_size(channel, timings->x_res, timings->y_res); + DSSDBG("pck %u\n", timings->pixel_clock); + DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", + t.hsw, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp); - xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; - ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; + DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); + } else { + enum dss_hdmi_venc_clk_source_select source; - ht = (timings->pixel_clock * 1000) / xtot; - vt = (timings->pixel_clock * 1000) / xtot / ytot; + source = dss_get_hdmi_venc_clk_source(); - DSSDBG("channel %d xres %u yres %u\n", channel, timings->x_res, - timings->y_res); - DSSDBG("pck %u\n", timings->pixel_clock); - DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", - timings->hsw, timings->hfp, timings->hbp, - timings->vsw, timings->vfp, timings->vbp); + if (source == DSS_VENC_TV_CLK) + t.y_res /= 2; + } - DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); + dispc_mgr_set_size(channel, t.x_res, t.y_res); } static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div, @@ -2411,6 +2739,7 @@ unsigned long dispc_fclk_rate(void) break; default: BUG(); + return 0; } return r; @@ -2441,6 +2770,7 @@ unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) break; default: BUG(); + return 0; } return r / lcd; @@ -2462,20 +2792,35 @@ unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) return r / pcd; } else { - struct omap_dss_device *dssdev = - dispc_mgr_get_device(channel); + enum dss_hdmi_venc_clk_source_select source; - switch (dssdev->type) { - case OMAP_DISPLAY_TYPE_VENC: + source = dss_get_hdmi_venc_clk_source(); + + switch (source) { + case DSS_VENC_TV_CLK: return venc_get_pixel_clock(); - case OMAP_DISPLAY_TYPE_HDMI: + case DSS_HDMI_M_PCLK: return hdmi_get_pixel_clock(); default: BUG(); + return 0; } } } +unsigned long dispc_core_clk_rate(void) +{ + int lcd; + unsigned long fclk = dispc_fclk_rate(); + + if (dss_has_feature(FEAT_CORE_CLK_DIV)) + lcd = REG_GET(DISPC_DIVISOR, 23, 16); + else + lcd = REG_GET(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD), 23, 16); + + return fclk / lcd; +} + void dispc_dump_clocks(struct seq_file *s) { int lcd, pcd; @@ -2588,7 +2933,7 @@ void dispc_dump_irqs(struct seq_file *s) } #endif -void dispc_dump_regs(struct seq_file *s) +static void dispc_dump_regs(struct seq_file *s) { int i, j; const char *mgr_names[] = { @@ -3247,27 +3592,6 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, return 0; } -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC -void dispc_fake_vsync_irq(void) -{ - u32 irqstatus = DISPC_IRQ_VSYNC; - int i; - - WARN_ON(!in_interrupt()); - - for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { - struct omap_dispc_isr_data *isr_data; - isr_data = &dispc.registered_isr[i]; - - if (!isr_data->isr) - continue; - - if (isr_data->mask & irqstatus) - isr_data->isr(isr_data->arg, irqstatus); - } -} -#endif - static void _omap_dispc_initialize_irq(void) { unsigned long flags; @@ -3330,7 +3654,7 @@ static void _omap_dispc_initial_config(void) } /* DISPC HW IP initialisation */ -static int omap_dispchw_probe(struct platform_device *pdev) +static int __init omap_dispchw_probe(struct platform_device *pdev) { u32 rev; int r = 0; @@ -3399,6 +3723,11 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc_runtime_put(); + dss_debugfs_create_file("dispc", dispc_dump_regs); + +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + dss_debugfs_create_file("dispc_irq", dispc_dump_irqs); +#endif return 0; err_runtime_get: @@ -3407,7 +3736,7 @@ err_runtime_get: return r; } -static int omap_dispchw_remove(struct platform_device *pdev) +static int __exit omap_dispchw_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); @@ -3419,19 +3748,12 @@ static int omap_dispchw_remove(struct platform_device *pdev) static int dispc_runtime_suspend(struct device *dev) { dispc_save_context(); - dss_runtime_put(); return 0; } static int dispc_runtime_resume(struct device *dev) { - int r; - - r = dss_runtime_get(); - if (r < 0) - return r; - dispc_restore_context(); return 0; @@ -3443,8 +3765,7 @@ static const struct dev_pm_ops dispc_pm_ops = { }; static struct platform_driver omap_dispchw_driver = { - .probe = omap_dispchw_probe, - .remove = omap_dispchw_remove, + .remove = __exit_p(omap_dispchw_remove), .driver = { .name = "omapdss_dispc", .owner = THIS_MODULE, @@ -3452,12 +3773,12 @@ static struct platform_driver omap_dispchw_driver = { }, }; -int dispc_init_platform_driver(void) +int __init dispc_init_platform_driver(void) { - return platform_driver_register(&omap_dispchw_driver); + return platform_driver_probe(&omap_dispchw_driver, omap_dispchw_probe); } -void dispc_uninit_platform_driver(void) +void __exit dispc_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dispchw_driver); + platform_driver_unregister(&omap_dispchw_driver); } diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index 5836bd1650f9..f278080e1063 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -120,6 +120,7 @@ static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel) return 0x03AC; default: BUG(); + return 0; } } @@ -134,6 +135,7 @@ static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel) return 0x03B0; default: BUG(); + return 0; } } @@ -144,10 +146,12 @@ static inline u16 DISPC_TIMING_H(enum omap_channel channel) return 0x0064; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0400; default: BUG(); + return 0; } } @@ -158,10 +162,12 @@ static inline u16 DISPC_TIMING_V(enum omap_channel channel) return 0x0068; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0404; default: BUG(); + return 0; } } @@ -172,10 +178,12 @@ static inline u16 DISPC_POL_FREQ(enum omap_channel channel) return 0x006C; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0408; default: BUG(); + return 0; } } @@ -186,10 +194,12 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel) return 0x0070; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x040C; default: BUG(); + return 0; } } @@ -205,6 +215,7 @@ static inline u16 DISPC_SIZE_MGR(enum omap_channel channel) return 0x03CC; default: BUG(); + return 0; } } @@ -215,10 +226,12 @@ static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel) return 0x01D4; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C0; default: BUG(); + return 0; } } @@ -229,10 +242,12 @@ static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel) return 0x01D8; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C4; default: BUG(); + return 0; } } @@ -243,10 +258,12 @@ static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel) return 0x01DC; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C8; default: BUG(); + return 0; } } @@ -257,10 +274,12 @@ static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel) return 0x0220; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03BC; default: BUG(); + return 0; } } @@ -271,10 +290,12 @@ static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel) return 0x0224; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B8; default: BUG(); + return 0; } } @@ -285,10 +306,12 @@ static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel) return 0x0228; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B4; default: BUG(); + return 0; } } @@ -306,6 +329,7 @@ static inline u16 DISPC_OVL_BASE(enum omap_plane plane) return 0x0300; default: BUG(); + return 0; } } @@ -321,6 +345,7 @@ static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane) return 0x0008; default: BUG(); + return 0; } } @@ -335,6 +360,7 @@ static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane) return 0x000C; default: BUG(); + return 0; } } @@ -343,6 +369,7 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0544; case OMAP_DSS_VIDEO2: @@ -351,6 +378,7 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) return 0x0310; default: BUG(); + return 0; } } @@ -359,6 +387,7 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0548; case OMAP_DSS_VIDEO2: @@ -367,6 +396,7 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) return 0x0314; default: BUG(); + return 0; } } @@ -381,6 +411,7 @@ static inline u16 DISPC_POS_OFFSET(enum omap_plane plane) return 0x009C; default: BUG(); + return 0; } } @@ -395,6 +426,7 @@ static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane) return 0x00A8; default: BUG(); + return 0; } } @@ -410,6 +442,7 @@ static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane) return 0x0070; default: BUG(); + return 0; } } @@ -418,6 +451,7 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0568; case OMAP_DSS_VIDEO2: @@ -426,6 +460,7 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) return 0x032C; default: BUG(); + return 0; } } @@ -441,6 +476,7 @@ static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane) return 0x008C; default: BUG(); + return 0; } } @@ -456,6 +492,7 @@ static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane) return 0x0088; default: BUG(); + return 0; } } @@ -471,6 +508,7 @@ static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane) return 0x00A4; default: BUG(); + return 0; } } @@ -486,6 +524,7 @@ static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane) return 0x0098; default: BUG(); + return 0; } } @@ -498,8 +537,10 @@ static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: BUG(); + return 0; default: BUG(); + return 0; } } @@ -512,8 +553,10 @@ static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: BUG(); + return 0; default: BUG(); + return 0; } } @@ -522,6 +565,7 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0024; @@ -529,6 +573,7 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) return 0x0090; default: BUG(); + return 0; } } @@ -537,6 +582,7 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0580; case OMAP_DSS_VIDEO2: @@ -545,6 +591,7 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) return 0x0424; default: BUG(); + return 0; } } @@ -553,6 +600,7 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0028; @@ -560,6 +608,7 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) return 0x0094; default: BUG(); + return 0; } } @@ -569,6 +618,7 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x002C; @@ -576,6 +626,7 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) return 0x0000; default: BUG(); + return 0; } } @@ -584,6 +635,7 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0584; case OMAP_DSS_VIDEO2: @@ -592,6 +644,7 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) return 0x0428; default: BUG(); + return 0; } } @@ -600,6 +653,7 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0030; @@ -607,6 +661,7 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) return 0x0004; default: BUG(); + return 0; } } @@ -615,6 +670,7 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0588; case OMAP_DSS_VIDEO2: @@ -623,6 +679,7 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) return 0x042C; default: BUG(); + return 0; } } @@ -632,6 +689,7 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0034 + i * 0x8; @@ -639,6 +697,7 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) return 0x0010 + i * 0x8; default: BUG(); + return 0; } } @@ -648,6 +707,7 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x058C + i * 0x8; case OMAP_DSS_VIDEO2: @@ -656,6 +716,7 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) return 0x0430 + i * 0x8; default: BUG(); + return 0; } } @@ -665,6 +726,7 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0038 + i * 0x8; @@ -672,6 +734,7 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) return 0x0014 + i * 0x8; default: BUG(); + return 0; } } @@ -681,6 +744,7 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0590 + i * 8; case OMAP_DSS_VIDEO2: @@ -689,6 +753,7 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) return 0x0434 + i * 0x8; default: BUG(); + return 0; } } @@ -698,12 +763,14 @@ static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: return 0x0074 + i * 0x4; default: BUG(); + return 0; } } @@ -713,6 +780,7 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0124 + i * 0x4; case OMAP_DSS_VIDEO2: @@ -721,6 +789,7 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) return 0x0050 + i * 0x4; default: BUG(); + return 0; } } @@ -730,6 +799,7 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x05CC + i * 0x4; case OMAP_DSS_VIDEO2: @@ -738,6 +808,7 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) return 0x0470 + i * 0x4; default: BUG(); + return 0; } } @@ -754,6 +825,7 @@ static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane) return 0x00A0; default: BUG(); + return 0; } } #endif diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 4424c198dbcd..249010630370 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -304,10 +304,18 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) return 24; default: BUG(); + return 0; } } EXPORT_SYMBOL(omapdss_default_get_recommended_bpp); +void omapdss_default_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} +EXPORT_SYMBOL(omapdss_default_get_timings); + /* Checks if replication logic should be used. Only use for active matrix, * when overlay is in RGB12U or RGB16 mode, and LCD interface is * 18bpp or 24bpp */ @@ -340,6 +348,7 @@ bool dss_use_replication(struct omap_dss_device *dssdev, break; default: BUG(); + return false; } return bpp > 16; @@ -352,46 +361,6 @@ void dss_init_device(struct platform_device *pdev, int i; int r; - switch (dssdev->type) { -#ifdef CONFIG_OMAP2_DSS_DPI - case OMAP_DISPLAY_TYPE_DPI: - r = dpi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_RFBI - case OMAP_DISPLAY_TYPE_DBI: - r = rfbi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - case OMAP_DISPLAY_TYPE_VENC: - r = venc_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_SDI - case OMAP_DISPLAY_TYPE_SDI: - r = sdi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - case OMAP_DISPLAY_TYPE_DSI: - r = dsi_init_display(dssdev); - break; -#endif - case OMAP_DISPLAY_TYPE_HDMI: - r = hdmi_init_display(dssdev); - break; - default: - DSSERR("Support for display '%s' not compiled in.\n", - dssdev->name); - return; - } - - if (r) { - DSSERR("failed to init display %s\n", dssdev->name); - return; - } - /* create device sysfs files */ i = 0; while ((attr = display_sysfs_attrs[i++]) != NULL) { diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index faaf305fda27..8c2056c9537b 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -156,7 +156,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) t->pixel_clock = pck; } - dispc_mgr_set_lcd_timings(dssdev->manager->id, t); + dss_mgr_set_timings(dssdev->manager, t); return 0; } @@ -202,10 +202,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) goto err_reg_enable; } - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) goto err_get_dispc; @@ -244,8 +240,6 @@ err_dsi_pll_init: err_get_dsi: dispc_runtime_put(); err_get_dispc: - dss_runtime_put(); -err_get_dss: if (cpu_is_omap34xx()) regulator_disable(dpi.vdds_dsi_reg); err_reg_enable: @@ -266,7 +260,6 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev) } dispc_runtime_put(); - dss_runtime_put(); if (cpu_is_omap34xx()) regulator_disable(dpi.vdds_dsi_reg); @@ -283,21 +276,15 @@ void dpi_set_timings(struct omap_dss_device *dssdev, DSSDBG("dpi_set_timings\n"); dssdev->panel.timings = *timings; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { - r = dss_runtime_get(); - if (r) - return; - r = dispc_runtime_get(); - if (r) { - dss_runtime_put(); + if (r) return; - } dpi_set_mode(dssdev); - dispc_mgr_go(dssdev->manager->id); dispc_runtime_put(); - dss_runtime_put(); + } else { + dss_mgr_set_timings(dssdev->manager, timings); } } EXPORT_SYMBOL(dpi_set_timings); @@ -312,7 +299,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, unsigned long pck; struct dispc_clock_info dispc_cinfo; - if (!dispc_lcd_timings_ok(timings)) + if (dss_mgr_check_timings(dssdev->manager, timings)) return -EINVAL; if (timings->pixel_clock == 0) @@ -352,7 +339,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(dpi_check_timings); -int dpi_init_display(struct omap_dss_device *dssdev) +static int __init dpi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -378,12 +365,58 @@ int dpi_init_display(struct omap_dss_device *dssdev) return 0; } -int dpi_init(void) +static void __init dpi_probe_pdata(struct platform_device *pdev) { + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DPI) + continue; + + r = dpi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + +static int __init omap_dpi_probe(struct platform_device *pdev) +{ + dpi_probe_pdata(pdev); + + return 0; +} + +static int __exit omap_dpi_remove(struct platform_device *pdev) +{ + omap_dss_unregister_child_devices(&pdev->dev); + return 0; } -void dpi_exit(void) +static struct platform_driver omap_dpi_driver = { + .remove = __exit_p(omap_dpi_remove), + .driver = { + .name = "omapdss_dpi", + .owner = THIS_MODULE, + }, +}; + +int __init dpi_init_platform_driver(void) { + return platform_driver_probe(&omap_dpi_driver, omap_dpi_probe); } +void __exit dpi_uninit_platform_driver(void) +{ + platform_driver_unregister(&omap_dpi_driver); +} diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 210a3c4f6150..ec363d8390ed 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -256,14 +256,13 @@ struct dsi_data { struct platform_device *pdev; void __iomem *base; + int module_id; + int irq; struct clk *dss_clk; struct clk *sys_clk; - int (*enable_pads)(int dsi_id, unsigned lane_mask); - void (*disable_pads)(int dsi_id, unsigned lane_mask); - struct dsi_clock_info current_cinfo; bool vdds_dsi_enabled; @@ -361,11 +360,6 @@ struct platform_device *dsi_get_dsidev_from_id(int module) return dsi_pdev_map[module]; } -static inline int dsi_get_dsidev_id(struct platform_device *dsidev) -{ - return dsidev->id; -} - static inline void dsi_write_reg(struct platform_device *dsidev, const struct dsi_reg idx, u32 val) { @@ -452,6 +446,7 @@ u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) return 16; default: BUG(); + return 0; } } @@ -1184,10 +1179,9 @@ static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev) static unsigned long dsi_fclk_rate(struct platform_device *dsidev) { unsigned long r; - int dsi_module = dsi_get_dsidev_id(dsidev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) { + if (dss_get_dsi_clk_source(dsi->module_id) == OMAP_DSS_CLK_SRC_FCK) { /* DSI FCLK source is DSS_CLK_FCK */ r = clk_get_rate(dsi->dss_clk); } else { @@ -1279,10 +1273,9 @@ static int dsi_pll_power(struct platform_device *dsidev, } /* calculate clock rates using dividers in cinfo */ -static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, +static int dsi_calc_clock_rates(struct platform_device *dsidev, struct dsi_clock_info *cinfo) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); if (cinfo->regn == 0 || cinfo->regn > dsi->regn_max) @@ -1297,21 +1290,8 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, if (cinfo->regm_dsi > dsi->regm_dsi_max) return -EINVAL; - if (cinfo->use_sys_clk) { - cinfo->clkin = clk_get_rate(dsi->sys_clk); - /* XXX it is unclear if highfreq should be used - * with DSS_SYS_CLK source also */ - cinfo->highfreq = 0; - } else { - cinfo->clkin = dispc_mgr_pclk_rate(dssdev->manager->id); - - if (cinfo->clkin < 32000000) - cinfo->highfreq = 0; - else - cinfo->highfreq = 1; - } - - cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1)); + cinfo->clkin = clk_get_rate(dsi->sys_clk); + cinfo->fint = cinfo->clkin / cinfo->regn; if (cinfo->fint > dsi->fint_max || cinfo->fint < dsi->fint_min) return -EINVAL; @@ -1378,27 +1358,21 @@ retry: memset(&cur, 0, sizeof(cur)); cur.clkin = dss_sys_clk; - cur.use_sys_clk = 1; - cur.highfreq = 0; - /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ - /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ + /* 0.75MHz < Fint = clkin / regn < 2.1MHz */ /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ for (cur.regn = 1; cur.regn < dsi->regn_max; ++cur.regn) { - if (cur.highfreq == 0) - cur.fint = cur.clkin / cur.regn; - else - cur.fint = cur.clkin / (2 * cur.regn); + cur.fint = cur.clkin / cur.regn; if (cur.fint > dsi->fint_max || cur.fint < dsi->fint_min) continue; - /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ + /* DSIPHY(MHz) = (2 * regm / regn) * clkin */ for (cur.regm = 1; cur.regm < dsi->regm_max; ++cur.regm) { unsigned long a, b; a = 2 * cur.regm * (cur.clkin/1000); - b = cur.regn * (cur.highfreq + 1); + b = cur.regn; cur.clkin4ddr = a / b * 1000; if (cur.clkin4ddr > 1800 * 1000 * 1000) @@ -1486,9 +1460,7 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, DSSDBGF(); - dsi->current_cinfo.use_sys_clk = cinfo->use_sys_clk; - dsi->current_cinfo.highfreq = cinfo->highfreq; - + dsi->current_cinfo.clkin = cinfo->clkin; dsi->current_cinfo.fint = cinfo->fint; dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr; dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk = @@ -1503,17 +1475,13 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, DSSDBG("DSI Fint %ld\n", cinfo->fint); - DSSDBG("clkin (%s) rate %ld, highfreq %d\n", - cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree", - cinfo->clkin, - cinfo->highfreq); + DSSDBG("clkin rate %ld\n", cinfo->clkin); /* DSIPHY == CLKIN4DDR */ - DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu / %d = %lu\n", + DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu = %lu\n", cinfo->regm, cinfo->regn, cinfo->clkin, - cinfo->highfreq + 1, cinfo->clkin4ddr); DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", @@ -1568,10 +1536,6 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ - l = FLD_MOD(l, cinfo->use_sys_clk ? 0 : 1, - 11, 11); /* DSI_PLL_CLKSEL */ - l = FLD_MOD(l, cinfo->highfreq, - 12, 12); /* DSI_PLL_HIGHFREQ */ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ @@ -1716,7 +1680,7 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct dsi_clock_info *cinfo = &dsi->current_cinfo; enum omap_dss_clk_source dispc_clk_src, dsi_clk_src; - int dsi_module = dsi_get_dsidev_id(dsidev); + int dsi_module = dsi->module_id; dispc_clk_src = dss_get_dispc_clk_source(); dsi_clk_src = dss_get_dsi_clk_source(dsi_module); @@ -1726,8 +1690,7 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1); - seq_printf(s, "dsi pll source = %s\n", - cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree"); + seq_printf(s, "dsi pll clkin\t%lu\n", cinfo->clkin); seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn); @@ -1789,7 +1752,6 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; struct dsi_irq_stats stats; - int dsi_module = dsi_get_dsidev_id(dsidev); spin_lock_irqsave(&dsi->irq_stats_lock, flags); @@ -1806,7 +1768,7 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, #define PIS(x) \ seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]); - seq_printf(s, "-- DSI%d interrupts --\n", dsi_module + 1); + seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1); PIS(VC0); PIS(VC1); PIS(VC2); @@ -1886,22 +1848,6 @@ static void dsi2_dump_irqs(struct seq_file *s) dsi_dump_dsidev_irqs(dsidev, s); } - -void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, - const struct file_operations *debug_fops) -{ - struct platform_device *dsidev; - - dsidev = dsi_get_dsidev_from_id(0); - if (dsidev) - debugfs_create_file("dsi1_irqs", S_IRUGO, debugfs_dir, - &dsi1_dump_irqs, debug_fops); - - dsidev = dsi_get_dsidev_from_id(1); - if (dsidev) - debugfs_create_file("dsi2_irqs", S_IRUGO, debugfs_dir, - &dsi2_dump_irqs, debug_fops); -} #endif static void dsi_dump_dsidev_regs(struct platform_device *dsidev, @@ -2002,21 +1948,6 @@ static void dsi2_dump_regs(struct seq_file *s) dsi_dump_dsidev_regs(dsidev, s); } -void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, - const struct file_operations *debug_fops) -{ - struct platform_device *dsidev; - - dsidev = dsi_get_dsidev_from_id(0); - if (dsidev) - debugfs_create_file("dsi1_regs", S_IRUGO, debugfs_dir, - &dsi1_dump_regs, debug_fops); - - dsidev = dsi_get_dsidev_from_id(1); - if (dsidev) - debugfs_create_file("dsi2_regs", S_IRUGO, debugfs_dir, - &dsi2_dump_regs, debug_fops); -} enum dsi_cio_power_state { DSI_COMPLEXIO_POWER_OFF = 0x0, DSI_COMPLEXIO_POWER_ON = 0x1, @@ -2073,6 +2004,7 @@ static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) return 1365 * 3; /* 1365x24 bits */ default: BUG(); + return 0; } } @@ -2337,7 +2269,7 @@ static int dsi_cio_init(struct omap_dss_device *dssdev) DSSDBGF(); - r = dsi->enable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + r = dss_dsi_enable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); if (r) return r; @@ -2447,7 +2379,7 @@ err_cio_pwr: dsi_cio_disable_lane_override(dsidev); err_scp_clk_dom: dsi_disable_scp_clk(dsidev); - dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); return r; } @@ -2461,7 +2393,7 @@ static void dsi_cio_uninit(struct omap_dss_device *dssdev) dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); dsi_disable_scp_clk(dsidev); - dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); } static void dsi_config_tx_fifo(struct platform_device *dsidev, @@ -2485,6 +2417,7 @@ static void dsi_config_tx_fifo(struct platform_device *dsidev, if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); BUG(); + return; } v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); @@ -2517,6 +2450,7 @@ static void dsi_config_rx_fifo(struct platform_device *dsidev, if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); BUG(); + return; } v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); @@ -2658,6 +2592,7 @@ static int dsi_sync_vc(struct platform_device *dsidev, int channel) return dsi_sync_vc_l4(dsidev, channel); default: BUG(); + return -EINVAL; } } @@ -3226,6 +3161,7 @@ static int dsi_vc_generic_send_read_request(struct omap_dss_device *dssdev, data = reqdata[0] | (reqdata[1] << 8); } else { BUG(); + return -EINVAL; } r = dsi_vc_send_short(dsidev, channel, data_type, data, 0); @@ -3340,7 +3276,6 @@ static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel, goto err; } - BUG(); err: DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel, type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS"); @@ -3735,6 +3670,186 @@ static void dsi_config_blanking_modes(struct omap_dss_device *dssdev) dsi_write_reg(dsidev, DSI_CTRL, r); } +/* + * According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3 + * results in maximum transition time for data and clock lanes to enter and + * exit HS mode. Hence, this is the scenario where the least amount of command + * mode data can be interleaved. We program the minimum amount of TXBYTECLKHS + * clock cycles that can be used to interleave command mode data in HS so that + * all scenarios are satisfied. + */ +static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs, + int exit_hs, int exiths_clk, int ddr_pre, int ddr_post) +{ + int transition; + + /* + * If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition + * time of data lanes only, if it isn't set, we need to consider HS + * transition time of both data and clock lanes. HS transition time + * of Scenario 3 is considered. + */ + if (ddr_alwon) { + transition = enter_hs + exit_hs + max(enter_hs, 2) + 1; + } else { + int trans1, trans2; + trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1; + trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre + + enter_hs + 1; + transition = max(trans1, trans2); + } + + return blank > transition ? blank - transition : 0; +} + +/* + * According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1 + * results in maximum transition time for data lanes to enter and exit LP mode. + * Hence, this is the scenario where the least amount of command mode data can + * be interleaved. We program the minimum amount of bytes that can be + * interleaved in LP so that all scenarios are satisfied. + */ +static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs, + int lp_clk_div, int tdsi_fclk) +{ + int trans_lp; /* time required for a LP transition, in TXBYTECLKHS */ + int tlp_avail; /* time left for interleaving commands, in CLKIN4DDR */ + int ttxclkesc; /* period of LP transmit escape clock, in CLKIN4DDR */ + int thsbyte_clk = 16; /* Period of TXBYTECLKHS clock, in CLKIN4DDR */ + int lp_inter; /* cmd mode data that can be interleaved, in bytes */ + + /* maximum LP transition time according to Scenario 1 */ + trans_lp = exit_hs + max(enter_hs, 2) + 1; + + /* CLKIN4DDR = 16 * TXBYTECLKHS */ + tlp_avail = thsbyte_clk * (blank - trans_lp); + + ttxclkesc = tdsi_fclk / lp_clk_div; + + lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc - + 26) / 16; + + return max(lp_inter, 0); +} + +static void dsi_config_cmd_mode_interleaving(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int blanking_mode; + int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode; + int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div; + int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat; + int tclk_trail, ths_exit, exiths_clk; + bool ddr_alwon; + struct omap_video_timings *timings = &dssdev->panel.timings; + int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt); + int ndl = dsi->num_lanes_used - 1; + int dsi_fclk_hsdiv = dssdev->clocks.dsi.regm_dsi + 1; + int hsa_interleave_hs = 0, hsa_interleave_lp = 0; + int hfp_interleave_hs = 0, hfp_interleave_lp = 0; + int hbp_interleave_hs = 0, hbp_interleave_lp = 0; + int bl_interleave_hs = 0, bl_interleave_lp = 0; + u32 r; + + r = dsi_read_reg(dsidev, DSI_CTRL); + blanking_mode = FLD_GET(r, 20, 20); + hfp_blanking_mode = FLD_GET(r, 21, 21); + hbp_blanking_mode = FLD_GET(r, 22, 22); + hsa_blanking_mode = FLD_GET(r, 23, 23); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING1); + hbp = FLD_GET(r, 11, 0); + hfp = FLD_GET(r, 23, 12); + hsa = FLD_GET(r, 31, 24); + + r = dsi_read_reg(dsidev, DSI_CLK_TIMING); + ddr_clk_post = FLD_GET(r, 7, 0); + ddr_clk_pre = FLD_GET(r, 15, 8); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING7); + exit_hs_mode_lat = FLD_GET(r, 15, 0); + enter_hs_mode_lat = FLD_GET(r, 31, 16); + + r = dsi_read_reg(dsidev, DSI_CLK_CTRL); + lp_clk_div = FLD_GET(r, 12, 0); + ddr_alwon = FLD_GET(r, 13, 13); + + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); + ths_exit = FLD_GET(r, 7, 0); + + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); + tclk_trail = FLD_GET(r, 15, 8); + + exiths_clk = ths_exit + tclk_trail; + + width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8); + bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl); + + if (!hsa_blanking_mode) { + hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + hsa_interleave_lp = dsi_compute_interleave_lp(hsa, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!hfp_blanking_mode) { + hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + hfp_interleave_lp = dsi_compute_interleave_lp(hfp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!hbp_blanking_mode) { + hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + + hbp_interleave_lp = dsi_compute_interleave_lp(hbp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!blanking_mode) { + bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + + bl_interleave_lp = dsi_compute_interleave_lp(bllp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n", + hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs, + bl_interleave_hs); + + DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n", + hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp, + bl_interleave_lp); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING4); + r = FLD_MOD(r, hsa_interleave_hs, 23, 16); + r = FLD_MOD(r, hfp_interleave_hs, 15, 8); + r = FLD_MOD(r, hbp_interleave_hs, 7, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING4, r); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING5); + r = FLD_MOD(r, hsa_interleave_lp, 23, 16); + r = FLD_MOD(r, hfp_interleave_lp, 15, 8); + r = FLD_MOD(r, hbp_interleave_lp, 7, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING5, r); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING6); + r = FLD_MOD(r, bl_interleave_hs, 31, 15); + r = FLD_MOD(r, bl_interleave_lp, 16, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING6, r); +} + static int dsi_proto_config(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -3769,6 +3884,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) break; default: BUG(); + return -EINVAL; } r = dsi_read_reg(dsidev, DSI_CTRL); @@ -3793,6 +3909,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) { dsi_config_vp_sync_events(dssdev); dsi_config_blanking_modes(dssdev); + dsi_config_cmd_mode_interleaving(dssdev); } dsi_vc_initial_config(dsidev, 0); @@ -4008,6 +4125,7 @@ int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) break; default: BUG(); + return -EINVAL; }; dsi_if_enable(dsidev, false); @@ -4192,10 +4310,6 @@ static void dsi_framedone_irq_callback(void *data, u32 mask) __cancel_delayed_work(&dsi->framedone_timeout_work); dsi_handle_framedone(dsidev, 0); - -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC - dispc_fake_vsync_irq(); -#endif } int omap_dsi_update(struct omap_dss_device *dssdev, int channel, @@ -4259,13 +4373,12 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) dispc_mgr_enable_stallmode(dssdev->manager->id, true); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1); - dispc_mgr_set_lcd_timings(dssdev->manager->id, &timings); + dss_mgr_set_timings(dssdev->manager, &timings); } else { dispc_mgr_enable_stallmode(dssdev->manager->id, false); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0); - dispc_mgr_set_lcd_timings(dssdev->manager->id, - &dssdev->panel.timings); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } dispc_mgr_set_lcd_display_type(dssdev->manager->id, @@ -4294,13 +4407,11 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) struct dsi_clock_info cinfo; int r; - /* we always use DSS_CLK_SYSCK as input clock */ - cinfo.use_sys_clk = true; cinfo.regn = dssdev->clocks.dsi.regn; cinfo.regm = dssdev->clocks.dsi.regm; cinfo.regm_dispc = dssdev->clocks.dsi.regm_dispc; cinfo.regm_dsi = dssdev->clocks.dsi.regm_dsi; - r = dsi_calc_clock_rates(dssdev, &cinfo); + r = dsi_calc_clock_rates(dsidev, &cinfo); if (r) { DSSERR("Failed to calc dsi clocks\n"); return r; @@ -4345,7 +4456,7 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) static int dsi_display_init_dsi(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - int dsi_module = dsi_get_dsidev_id(dsidev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; r = dsi_pll_init(dsidev, true, true); @@ -4357,7 +4468,7 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) goto err1; dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); - dss_select_dsi_clk_source(dsi_module, dssdev->clocks.dsi.dsi_fclk_src); + dss_select_dsi_clk_source(dsi->module_id, dssdev->clocks.dsi.dsi_fclk_src); dss_select_lcd_clk_source(dssdev->manager->id, dssdev->clocks.dispc.channel.lcd_clk_src); @@ -4396,7 +4507,7 @@ err3: dsi_cio_uninit(dssdev); err2: dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); - dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK); err1: @@ -4410,7 +4521,6 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int dsi_module = dsi_get_dsidev_id(dsidev); if (enter_ulps && !dsi->ulps_enabled) dsi_enter_ulps(dsidev); @@ -4423,7 +4533,7 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, dsi_vc_enable(dsidev, 3, 0); dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); - dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK); dsi_cio_uninit(dssdev); dsi_pll_uninit(dsidev, disconnect_lanes); @@ -4527,7 +4637,7 @@ int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) } EXPORT_SYMBOL(omapdss_dsi_enable_te); -int dsi_init_display(struct omap_dss_device *dssdev) +static int __init dsi_init_display(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); @@ -4680,13 +4790,39 @@ static void dsi_put_clocks(struct platform_device *dsidev) clk_put(dsi->sys_clk); } +static void __init dsi_probe_pdata(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct omap_dss_board_info *pdata = dsidev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) + continue; + + if (dssdev->phy.dsi.module != dsi->module_id) + continue; + + r = dsi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &dsidev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* DSI1 HW IP initialisation */ -static int omap_dsihw_probe(struct platform_device *dsidev) +static int __init omap_dsihw_probe(struct platform_device *dsidev) { - struct omap_display_platform_data *dss_plat_data; - struct omap_dss_board_info *board_info; u32 rev; - int r, i, dsi_module = dsi_get_dsidev_id(dsidev); + int r, i; struct resource *dsi_mem; struct dsi_data *dsi; @@ -4694,15 +4830,11 @@ static int omap_dsihw_probe(struct platform_device *dsidev) if (!dsi) return -ENOMEM; + dsi->module_id = dsidev->id; dsi->pdev = dsidev; - dsi_pdev_map[dsi_module] = dsidev; + dsi_pdev_map[dsi->module_id] = dsidev; dev_set_drvdata(&dsidev->dev, dsi); - dss_plat_data = dsidev->dev.platform_data; - board_info = dss_plat_data->board_data; - dsi->enable_pads = board_info->dsi_enable_pads; - dsi->disable_pads = board_info->dsi_disable_pads; - spin_lock_init(&dsi->irq_lock); spin_lock_init(&dsi->errors_lock); dsi->errors = 0; @@ -4780,8 +4912,21 @@ static int omap_dsihw_probe(struct platform_device *dsidev) else dsi->num_lanes_supported = 3; + dsi_probe_pdata(dsidev); + dsi_runtime_put(dsidev); + if (dsi->module_id == 0) + dss_debugfs_create_file("dsi1_regs", dsi1_dump_regs); + else if (dsi->module_id == 1) + dss_debugfs_create_file("dsi2_regs", dsi2_dump_regs); + +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + if (dsi->module_id == 0) + dss_debugfs_create_file("dsi1_irqs", dsi1_dump_irqs); + else if (dsi->module_id == 1) + dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs); +#endif return 0; err_runtime_get: @@ -4790,12 +4935,14 @@ err_runtime_get: return r; } -static int omap_dsihw_remove(struct platform_device *dsidev) +static int __exit omap_dsihw_remove(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); WARN_ON(dsi->scp_clk_refcount > 0); + omap_dss_unregister_child_devices(&dsidev->dev); + pm_runtime_disable(&dsidev->dev); dsi_put_clocks(dsidev); @@ -4816,7 +4963,6 @@ static int omap_dsihw_remove(struct platform_device *dsidev) static int dsi_runtime_suspend(struct device *dev) { dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -4825,20 +4971,11 @@ static int dsi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) - goto err_get_dispc; + return r; return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops dsi_pm_ops = { @@ -4847,8 +4984,7 @@ static const struct dev_pm_ops dsi_pm_ops = { }; static struct platform_driver omap_dsihw_driver = { - .probe = omap_dsihw_probe, - .remove = omap_dsihw_remove, + .remove = __exit_p(omap_dsihw_remove), .driver = { .name = "omapdss_dsi", .owner = THIS_MODULE, @@ -4856,12 +4992,12 @@ static struct platform_driver omap_dsihw_driver = { }, }; -int dsi_init_platform_driver(void) +int __init dsi_init_platform_driver(void) { - return platform_driver_register(&omap_dsihw_driver); + return platform_driver_probe(&omap_dsihw_driver, omap_dsihw_probe); } -void dsi_uninit_platform_driver(void) +void __exit dsi_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dsihw_driver); + platform_driver_unregister(&omap_dsihw_driver); } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index bd2d5e159463..6ea1ff149f6f 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -62,6 +62,9 @@ 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); + static struct { struct platform_device *pdev; void __iomem *base; @@ -277,7 +280,7 @@ void dss_dump_clocks(struct seq_file *s) dss_runtime_put(); } -void dss_dump_regs(struct seq_file *s) +static void dss_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) @@ -322,6 +325,7 @@ void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) break; default: BUG(); + return; } dss_feat_get_reg_field(FEAT_REG_DISPC_CLK_SWITCH, &start, &end); @@ -335,7 +339,7 @@ void dss_select_dsi_clk_source(int dsi_module, enum omap_dss_clk_source clk_src) { struct platform_device *dsidev; - int b; + int b, pos; switch (clk_src) { case OMAP_DSS_CLK_SRC_FCK: @@ -355,9 +359,11 @@ void dss_select_dsi_clk_source(int dsi_module, break; default: BUG(); + return; } - REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ + pos = dsi_module == 0 ? 1 : 10; + REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* DSIx_CLK_SWITCH */ dss.dsi_clk_source[dsi_module] = clk_src; } @@ -389,6 +395,7 @@ void dss_select_lcd_clk_source(enum omap_channel channel, break; default: BUG(); + return; } pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12; @@ -706,7 +713,7 @@ static void dss_put_clocks(void) clk_put(dss.dss_clk); } -int dss_runtime_get(void) +static int dss_runtime_get(void) { int r; @@ -717,7 +724,7 @@ int dss_runtime_get(void) return r < 0 ? r : 0; } -void dss_runtime_put(void) +static void dss_runtime_put(void) { int r; @@ -740,7 +747,7 @@ void dss_debug_dump_clocks(struct seq_file *s) #endif /* DSS HW IP initialisation */ -static int omap_dsshw_probe(struct platform_device *pdev) +static int __init omap_dsshw_probe(struct platform_device *pdev) { struct resource *dss_mem; u32 rev; @@ -785,40 +792,24 @@ static int omap_dsshw_probe(struct platform_device *pdev) dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; - r = dpi_init(); - if (r) { - DSSERR("Failed to initialize DPI\n"); - goto err_dpi; - } - - r = sdi_init(); - if (r) { - DSSERR("Failed to initialize SDI\n"); - goto err_sdi; - } - 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)); dss_runtime_put(); + dss_debugfs_create_file("dss", dss_dump_regs); + return 0; -err_sdi: - dpi_exit(); -err_dpi: - dss_runtime_put(); + err_runtime_get: pm_runtime_disable(&pdev->dev); dss_put_clocks(); return r; } -static int omap_dsshw_remove(struct platform_device *pdev) +static int __exit omap_dsshw_remove(struct platform_device *pdev) { - dpi_exit(); - sdi_exit(); - pm_runtime_disable(&pdev->dev); dss_put_clocks(); @@ -829,11 +820,24 @@ static int omap_dsshw_remove(struct platform_device *pdev) static int dss_runtime_suspend(struct device *dev) { dss_save_context(); + dss_set_min_bus_tput(dev, 0); return 0; } static int dss_runtime_resume(struct device *dev) { + int r; + /* + * Set an arbitrarily high tput request to ensure OPP100. + * What we should really do is to make a request to stay in OPP100, + * without any tput requirements, but that is not currently possible + * via the PM layer. + */ + + r = dss_set_min_bus_tput(dev, 1000000000); + if (r) + return r; + dss_restore_context(); return 0; } @@ -844,8 +848,7 @@ static const struct dev_pm_ops dss_pm_ops = { }; static struct platform_driver omap_dsshw_driver = { - .probe = omap_dsshw_probe, - .remove = omap_dsshw_remove, + .remove = __exit_p(omap_dsshw_remove), .driver = { .name = "omapdss_dss", .owner = THIS_MODULE, @@ -853,12 +856,12 @@ static struct platform_driver omap_dsshw_driver = { }, }; -int dss_init_platform_driver(void) +int __init dss_init_platform_driver(void) { - return platform_driver_register(&omap_dsshw_driver); + return platform_driver_probe(&omap_dsshw_driver, omap_dsshw_probe); } void dss_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dsshw_driver); + platform_driver_unregister(&omap_dsshw_driver); } diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index d4b3dff2ead3..dd1092ceaeef 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -150,9 +150,6 @@ struct dsi_clock_info { u16 regm_dsi; /* OMAP3: REGM4 * OMAP4: REGM5 */ u16 lp_clk_div; - - u8 highfreq; - bool use_sys_clk; }; struct seq_file; @@ -162,6 +159,16 @@ struct platform_device; struct bus_type *dss_get_bus(void); struct regulator *dss_get_vdds_dsi(void); struct regulator *dss_get_vdds_sdi(void); +int dss_get_ctx_loss_count(struct device *dev); +int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask); +void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); +int dss_set_min_bus_tput(struct device *dev, unsigned long tput); +int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); + +int omap_dss_register_device(struct omap_dss_device *dssdev, + struct device *parent, int disp_num); +void omap_dss_unregister_device(struct omap_dss_device *dssdev); +void omap_dss_unregister_child_devices(struct device *parent); /* apply */ void dss_apply_init(void); @@ -179,6 +186,9 @@ void dss_mgr_get_info(struct omap_overlay_manager *mgr, int dss_mgr_set_device(struct omap_overlay_manager *mgr, struct omap_dss_device *dssdev); int dss_mgr_unset_device(struct omap_overlay_manager *mgr); +void dss_mgr_set_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings); +const struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr); bool dss_ovl_is_enabled(struct omap_overlay *ovl); int dss_ovl_enable(struct omap_overlay *ovl); @@ -208,9 +218,11 @@ int dss_init_overlay_managers(struct platform_device *pdev); void dss_uninit_overlay_managers(struct platform_device *pdev); int dss_mgr_simple_check(struct omap_overlay_manager *mgr, const struct omap_overlay_manager_info *info); +int dss_mgr_check_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings); int dss_mgr_check(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, struct omap_overlay_manager_info *info, + const struct omap_video_timings *mgr_timings, struct omap_overlay_info **overlay_infos); /* overlay */ @@ -220,22 +232,18 @@ void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); int dss_ovl_simple_check(struct omap_overlay *ovl, const struct omap_overlay_info *info); -int dss_ovl_check(struct omap_overlay *ovl, - struct omap_overlay_info *info, struct omap_dss_device *dssdev); +int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, + const struct omap_video_timings *mgr_timings); /* DSS */ -int dss_init_platform_driver(void); +int dss_init_platform_driver(void) __init; void dss_uninit_platform_driver(void); -int dss_runtime_get(void); -void dss_runtime_put(void); - void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); 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); -void dss_dump_regs(struct seq_file *s); #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) void dss_debug_dump_clocks(struct seq_file *s); #endif @@ -265,19 +273,8 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck, struct dispc_clock_info *dispc_cinfo); /* SDI */ -#ifdef CONFIG_OMAP2_DSS_SDI -int sdi_init(void); -void sdi_exit(void); -int sdi_init_display(struct omap_dss_device *display); -#else -static inline int sdi_init(void) -{ - return 0; -} -static inline void sdi_exit(void) -{ -} -#endif +int sdi_init_platform_driver(void) __init; +void sdi_uninit_platform_driver(void) __exit; /* DSI */ #ifdef CONFIG_OMAP2_DSS_DSI @@ -285,19 +282,14 @@ static inline void sdi_exit(void) struct dentry; struct file_operations; -int dsi_init_platform_driver(void); -void dsi_uninit_platform_driver(void); +int dsi_init_platform_driver(void) __init; +void dsi_uninit_platform_driver(void) __exit; int dsi_runtime_get(struct platform_device *dsidev); void dsi_runtime_put(struct platform_device *dsidev); void dsi_dump_clocks(struct seq_file *s); -void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, - const struct file_operations *debug_fops); -void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, - const struct file_operations *debug_fops); -int dsi_init_display(struct omap_dss_device *display); void dsi_irq_handler(void); u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt); @@ -314,13 +306,6 @@ void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); struct platform_device *dsi_get_dsidev_from_id(int module); #else -static inline int dsi_init_platform_driver(void) -{ - return 0; -} -static inline void dsi_uninit_platform_driver(void) -{ -} static inline int dsi_runtime_get(struct platform_device *dsidev) { return 0; @@ -377,28 +362,14 @@ static inline struct platform_device *dsi_get_dsidev_from_id(int module) #endif /* DPI */ -#ifdef CONFIG_OMAP2_DSS_DPI -int dpi_init(void); -void dpi_exit(void); -int dpi_init_display(struct omap_dss_device *dssdev); -#else -static inline int dpi_init(void) -{ - return 0; -} -static inline void dpi_exit(void) -{ -} -#endif +int dpi_init_platform_driver(void) __init; +void dpi_uninit_platform_driver(void) __exit; /* DISPC */ -int dispc_init_platform_driver(void); -void dispc_uninit_platform_driver(void); +int dispc_init_platform_driver(void) __init; +void dispc_uninit_platform_driver(void) __exit; void dispc_dump_clocks(struct seq_file *s); -void dispc_dump_irqs(struct seq_file *s); -void dispc_dump_regs(struct seq_file *s); void dispc_irq_handler(void); -void dispc_fake_vsync_irq(void); int dispc_runtime_get(void); void dispc_runtime_put(void); @@ -409,12 +380,12 @@ void dispc_disable_sidle(void); void dispc_lcd_enable_signal_polarity(bool act_high); void dispc_lcd_enable_signal(bool enable); void dispc_pck_free_enable(bool enable); -void dispc_set_digit_size(u16 width, u16 height); void dispc_enable_fifomerge(bool enable); void dispc_enable_gamma_table(bool enable); void dispc_set_loadmode(enum omap_dss_load_mode mode); -bool dispc_lcd_timings_ok(struct omap_video_timings *timings); +bool dispc_mgr_timings_ok(enum omap_channel channel, + const struct omap_video_timings *timings); unsigned long dispc_fclk_rate(void); void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, struct dispc_clock_info *cinfo); @@ -424,15 +395,16 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, - u32 *fifo_low, u32 *fifo_high, bool use_fifomerge); + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, + bool manual_update); int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, - bool ilace, bool replication); + bool ilace, bool replication, + const struct omap_video_timings *mgr_timings); int dispc_ovl_enable(enum omap_plane plane, bool enable); void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel); void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable); -void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height); u32 dispc_mgr_get_vsync_irq(enum omap_channel channel); u32 dispc_mgr_get_framedone_irq(enum omap_channel channel); bool dispc_mgr_go_busy(enum omap_channel channel); @@ -445,12 +417,13 @@ void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable); void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines); void dispc_mgr_set_lcd_display_type(enum omap_channel channel, enum omap_lcd_display_type type); -void dispc_mgr_set_lcd_timings(enum omap_channel channel, +void dispc_mgr_set_timings(enum omap_channel channel, struct omap_video_timings *timings); void dispc_mgr_set_pol_freq(enum omap_channel channel, enum omap_panel_config config, u8 acbi, u8 acb); unsigned long dispc_mgr_lclk_rate(enum omap_channel channel); unsigned long dispc_mgr_pclk_rate(enum omap_channel channel); +unsigned long dispc_core_clk_rate(void); int dispc_mgr_set_clock_div(enum omap_channel channel, struct dispc_clock_info *cinfo); int dispc_mgr_get_clock_div(enum omap_channel channel, @@ -460,19 +433,10 @@ void dispc_mgr_setup(enum omap_channel channel, /* VENC */ #ifdef CONFIG_OMAP2_DSS_VENC -int venc_init_platform_driver(void); -void venc_uninit_platform_driver(void); -void venc_dump_regs(struct seq_file *s); -int venc_init_display(struct omap_dss_device *display); +int venc_init_platform_driver(void) __init; +void venc_uninit_platform_driver(void) __exit; unsigned long venc_get_pixel_clock(void); #else -static inline int venc_init_platform_driver(void) -{ - return 0; -} -static inline void venc_uninit_platform_driver(void) -{ -} static inline unsigned long venc_get_pixel_clock(void) { WARN("%s: VENC not compiled in, returning pclk as 0\n", __func__); @@ -482,23 +446,10 @@ static inline unsigned long venc_get_pixel_clock(void) /* HDMI */ #ifdef CONFIG_OMAP4_DSS_HDMI -int hdmi_init_platform_driver(void); -void hdmi_uninit_platform_driver(void); -int hdmi_init_display(struct omap_dss_device *dssdev); +int hdmi_init_platform_driver(void) __init; +void hdmi_uninit_platform_driver(void) __exit; unsigned long hdmi_get_pixel_clock(void); -void hdmi_dump_regs(struct seq_file *s); #else -static inline int hdmi_init_display(struct omap_dss_device *dssdev) -{ - return 0; -} -static inline int hdmi_init_platform_driver(void) -{ - return 0; -} -static inline void hdmi_uninit_platform_driver(void) -{ -} static inline unsigned long hdmi_get_pixel_clock(void) { WARN("%s: HDMI not compiled in, returning pclk as 0\n", __func__); @@ -514,22 +465,18 @@ int omapdss_hdmi_read_edid(u8 *buf, int len); bool omapdss_hdmi_detect(void); int hdmi_panel_init(void); void hdmi_panel_exit(void); +#ifdef CONFIG_OMAP4_DSS_HDMI_AUDIO +int hdmi_audio_enable(void); +void hdmi_audio_disable(void); +int hdmi_audio_start(void); +void hdmi_audio_stop(void); +bool hdmi_mode_has_audio(void); +int hdmi_audio_config(struct omap_dss_audio *audio); +#endif /* RFBI */ -#ifdef CONFIG_OMAP2_DSS_RFBI -int rfbi_init_platform_driver(void); -void rfbi_uninit_platform_driver(void); -void rfbi_dump_regs(struct seq_file *s); -int rfbi_init_display(struct omap_dss_device *display); -#else -static inline int rfbi_init_platform_driver(void) -{ - return 0; -} -static inline void rfbi_uninit_platform_driver(void) -{ -} -#endif +int rfbi_init_platform_driver(void) __init; +void rfbi_uninit_platform_driver(void) __exit; #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index ce14aa6dd672..938709724f0c 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -52,6 +52,8 @@ struct omap_dss_features { const char * const *clksrc_names; const struct dss_param_range *dss_params; + const enum omap_dss_rotation_type supported_rotation_types; + const u32 buffer_size_unit; const u32 burst_size_unit; }; @@ -311,6 +313,8 @@ static const struct dss_param_range omap2_dss_param_range[] = { * scaler cannot scale a image with width more than 768. */ [FEAT_PARAM_LINEWIDTH] = { 1, 768 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const struct dss_param_range omap3_dss_param_range[] = { @@ -324,6 +328,8 @@ static const struct dss_param_range omap3_dss_param_range[] = { [FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1}, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, [FEAT_PARAM_LINEWIDTH] = { 1, 1024 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const struct dss_param_range omap4_dss_param_range[] = { @@ -337,6 +343,8 @@ static const struct dss_param_range omap4_dss_param_range[] = { [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, [FEAT_PARAM_LINEWIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const enum dss_feat_id omap2_dss_feat_list[] = { @@ -399,6 +407,7 @@ static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { @@ -416,6 +425,7 @@ static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; static const enum dss_feat_id omap4_dss_feat_list[] = { @@ -434,6 +444,7 @@ static const enum dss_feat_id omap4_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; /* OMAP2 DSS Features */ @@ -451,6 +462,7 @@ static const struct omap_dss_features omap2_dss_features = { .overlay_caps = omap2_dss_overlay_caps, .clksrc_names = omap2_dss_clk_source_names, .dss_params = omap2_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -470,6 +482,7 @@ static const struct omap_dss_features omap3430_dss_features = { .overlay_caps = omap3430_dss_overlay_caps, .clksrc_names = omap3_dss_clk_source_names, .dss_params = omap3_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -488,6 +501,7 @@ static const struct omap_dss_features omap3630_dss_features = { .overlay_caps = omap3630_dss_overlay_caps, .clksrc_names = omap3_dss_clk_source_names, .dss_params = omap3_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -508,6 +522,7 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -527,6 +542,7 @@ static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -546,6 +562,7 @@ static const struct omap_dss_features omap4_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -562,13 +579,17 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .pll_enable = ti_hdmi_4xxx_pll_enable, .pll_disable = ti_hdmi_4xxx_pll_disable, .video_enable = ti_hdmi_4xxx_wp_video_start, + .video_disable = ti_hdmi_4xxx_wp_video_stop, .dump_wrapper = ti_hdmi_4xxx_wp_dump, .dump_core = ti_hdmi_4xxx_core_dump, .dump_pll = ti_hdmi_4xxx_pll_dump, .dump_phy = ti_hdmi_4xxx_phy_dump, -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) .audio_enable = ti_hdmi_4xxx_wp_audio_enable, + .audio_disable = ti_hdmi_4xxx_wp_audio_disable, + .audio_start = ti_hdmi_4xxx_audio_start, + .audio_stop = ti_hdmi_4xxx_audio_stop, + .audio_config = ti_hdmi_4xxx_audio_config, #endif }; @@ -662,6 +683,11 @@ void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) *end = omap_current_dss_features->reg_fields[id].end; } +bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type) +{ + return omap_current_dss_features->supported_rotation_types & rot_type; +} + void dss_features_init(void) { if (cpu_is_omap24xx()) diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index c332e7ddfce1..bdf469f080e7 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -62,6 +62,7 @@ enum dss_feat_id { FEAT_FIFO_MERGE, /* An unknown HW bug causing the normal FIFO thresholds not to work */ FEAT_OMAP3_DSI_FIFO_BUG, + FEAT_BURST_2D, }; /* DSS register field id */ @@ -91,6 +92,8 @@ enum dss_range_param { FEAT_PARAM_DSIPLL_LPDIV, FEAT_PARAM_DOWNSCALE, FEAT_PARAM_LINEWIDTH, + FEAT_PARAM_MGR_WIDTH, + FEAT_PARAM_MGR_HEIGHT, }; /* DSS Feature Functions */ @@ -108,6 +111,8 @@ const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id); u32 dss_feat_get_buffer_size_unit(void); /* in bytes */ u32 dss_feat_get_burst_size_unit(void); /* in bytes */ +bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type); + bool dss_has_feature(enum dss_feat_id id); void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); void dss_features_init(void); diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index c4b4f6950a92..8195c7166d20 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -33,12 +33,6 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <video/omapdss.h> -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -#include <sound/soc.h> -#include <sound/pcm_params.h> -#include "ti_hdmi_4xxx_ip.h" -#endif #include "ti_hdmi.h" #include "dss.h" @@ -63,7 +57,6 @@ static struct { struct mutex lock; - struct omap_display_platform_data *pdata; struct platform_device *pdev; struct hdmi_ip_data ip_data; @@ -130,25 +123,12 @@ static int hdmi_runtime_get(void) DSSDBG("hdmi_runtime_get\n"); - /* - * HACK: Add dss_runtime_get() to ensure DSS clock domain is enabled. - * This should be removed later. - */ - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = pm_runtime_get_sync(&hdmi.pdev->dev); WARN_ON(r < 0); if (r < 0) - goto err_get_hdmi; + return r; return 0; - -err_get_hdmi: - dss_runtime_put(); -err_get_dss: - return r; } static void hdmi_runtime_put(void) @@ -159,15 +139,9 @@ static void hdmi_runtime_put(void) r = pm_runtime_put_sync(&hdmi.pdev->dev); WARN_ON(r < 0); - - /* - * HACK: This is added to complement the dss_runtime_get() call in - * hdmi_runtime_get(). This should be removed later. - */ - dss_runtime_put(); } -int hdmi_init_display(struct omap_dss_device *dssdev) +static int __init hdmi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -344,7 +318,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); /* config the PLL and PHY hdmi_set_pll_pwrfirst */ r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data); @@ -376,10 +350,11 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dispc_enable_gamma_table(0); /* tv size */ - dispc_set_digit_size(dssdev->panel.timings.x_res, - dssdev->panel.timings.y_res); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 1); + r = hdmi.ip_data.ops->video_enable(&hdmi.ip_data); + if (r) + goto err_vid_enable; r = dss_mgr_enable(dssdev->manager); if (r) @@ -388,7 +363,8 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) return 0; err_mgr_enable: - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); +err_vid_enable: hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); err: @@ -400,7 +376,7 @@ static void hdmi_power_off(struct omap_dss_device *dssdev) { dss_mgr_disable(dssdev->manager); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); hdmi_runtime_put(); @@ -436,10 +412,12 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev) r = hdmi_power_on(dssdev); if (r) DSSERR("failed to power on device\n"); + } else { + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } } -void hdmi_dump_regs(struct seq_file *s) +static void hdmi_dump_regs(struct seq_file *s) { mutex_lock(&hdmi.lock); @@ -555,248 +533,201 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi.lock); } -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - -static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) +static int hdmi_get_clocks(struct platform_device *pdev) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct platform_device *pdev = to_platform_device(codec->dev); - struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec); - int err = 0; + struct clk *clk; - if (!(ip_data->ops) && !(ip_data->ops->audio_enable)) { - dev_err(&pdev->dev, "Cannot enable/disable audio\n"); - return -ENODEV; + clk = clk_get(&pdev->dev, "sys_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get sys_clk\n"); + return PTR_ERR(clk); } - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ip_data->ops->audio_enable(ip_data, true); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ip_data->ops->audio_enable(ip_data, false); - break; - default: - err = -EINVAL; - } - return err; -} - -static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec); - struct hdmi_audio_format audio_format; - struct hdmi_audio_dma audio_dma; - struct hdmi_core_audio_config core_cfg; - struct hdmi_core_infoframe_audio aud_if_cfg; - int err, n, cts; - enum hdmi_core_audio_sample_freq sample_freq; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - core_cfg.i2s_cfg.word_max_length = - HDMI_AUDIO_I2S_MAX_WORD_20BITS; - core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_16_BITS; - core_cfg.i2s_cfg.in_length_bits = - HDMI_AUDIO_I2S_INPUT_LENGTH_16; - core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; - audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; - audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; - audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; - audio_dma.transfer_size = 0x10; - break; - case SNDRV_PCM_FORMAT_S24_LE: - core_cfg.i2s_cfg.word_max_length = - HDMI_AUDIO_I2S_MAX_WORD_24BITS; - core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_24_BITS; - core_cfg.i2s_cfg.in_length_bits = - HDMI_AUDIO_I2S_INPUT_LENGTH_24; - audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; - audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; - audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; - core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; - audio_dma.transfer_size = 0x20; - break; - default: + hdmi.sys_clk = clk; + + return 0; +} + +static void hdmi_put_clocks(void) +{ + if (hdmi.sys_clk) + clk_put(hdmi.sys_clk); +} + +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts) +{ + u32 deep_color; + bool deep_color_correct = false; + u32 pclk = hdmi.ip_data.cfg.timings.pixel_clock; + + if (n == NULL || cts == NULL) return -EINVAL; - } - switch (params_rate(params)) { + /* TODO: When implemented, query deep color mode here. */ + deep_color = 100; + + /* + * When using deep color, the default N value (as in the HDMI + * specification) yields to an non-integer CTS. Hence, we + * modify it while keeping the restrictions described in + * section 7.2.1 of the HDMI 1.4a specification. + */ + switch (sample_freq) { case 32000: - sample_freq = HDMI_AUDIO_FS_32000; + case 48000: + case 96000: + case 192000: + if (deep_color == 125) + if (pclk == 27027 || pclk == 74250) + deep_color_correct = true; + if (deep_color == 150) + if (pclk == 27027) + deep_color_correct = true; break; case 44100: - sample_freq = HDMI_AUDIO_FS_44100; - break; - case 48000: - sample_freq = HDMI_AUDIO_FS_48000; + case 88200: + case 176400: + if (deep_color == 125) + if (pclk == 27027) + deep_color_correct = true; break; default: return -EINVAL; } - err = hdmi_config_audio_acr(ip_data, params_rate(params), &n, &cts); - if (err < 0) - return err; - - /* Audio wrapper config */ - audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; - audio_format.active_chnnls_msk = 0x03; - audio_format.type = HDMI_AUDIO_TYPE_LPCM; - audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; - /* Disable start/stop signals of IEC 60958 blocks */ - audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF; + if (deep_color_correct) { + switch (sample_freq) { + case 32000: + *n = 8192; + break; + case 44100: + *n = 12544; + break; + case 48000: + *n = 8192; + break; + case 88200: + *n = 25088; + break; + case 96000: + *n = 16384; + break; + case 176400: + *n = 50176; + break; + case 192000: + *n = 32768; + break; + default: + return -EINVAL; + } + } else { + switch (sample_freq) { + case 32000: + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + *n = 6144; + break; + case 88200: + *n = 12544; + break; + case 96000: + *n = 12288; + break; + case 176400: + *n = 25088; + break; + case 192000: + *n = 24576; + break; + default: + return -EINVAL; + } + } + /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ + *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); - audio_dma.block_size = 0xC0; - audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; - audio_dma.fifo_threshold = 0x20; /* in number of samples */ + return 0; +} - hdmi_wp_audio_config_dma(ip_data, &audio_dma); - hdmi_wp_audio_config_format(ip_data, &audio_format); +int hdmi_audio_enable(void) +{ + DSSDBG("audio_enable\n"); - /* - * I2S config - */ - core_cfg.i2s_cfg.en_high_bitrate_aud = false; - /* Only used with high bitrate audio */ - core_cfg.i2s_cfg.cbit_order = false; - /* Serial data and word select should change on sck rising edge */ - core_cfg.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; - core_cfg.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; - /* Set I2S word select polarity */ - core_cfg.i2s_cfg.ws_polarity = HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT; - core_cfg.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; - /* Set serial data to word select shift. See Phillips spec. */ - core_cfg.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; - /* Enable one of the four available serial data channels */ - core_cfg.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; - - /* Core audio config */ - core_cfg.freq_sample = sample_freq; - core_cfg.n = n; - core_cfg.cts = cts; - if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { - core_cfg.aud_par_busclk = 0; - core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; - core_cfg.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); - } else { - core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); - core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; - core_cfg.use_mclk = true; - } + return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data); +} - if (core_cfg.use_mclk) - core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; - core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; - core_cfg.en_spdif = false; - /* Use sample frequency from channel status word */ - core_cfg.fs_override = true; - /* Enable ACR packets */ - core_cfg.en_acr_pkt = true; - /* Disable direct streaming digital audio */ - core_cfg.en_dsd_audio = false; - /* Use parallel audio interface */ - core_cfg.en_parallel_aud_input = true; - - hdmi_core_audio_config(ip_data, &core_cfg); +void hdmi_audio_disable(void) +{ + DSSDBG("audio_disable\n"); - /* - * Configure packet - * info frame audio see doc CEA861-D page 74 - */ - aud_if_cfg.db1_coding_type = HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM; - aud_if_cfg.db1_channel_count = 2; - aud_if_cfg.db2_sample_freq = HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM; - aud_if_cfg.db2_sample_size = HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM; - aud_if_cfg.db4_channel_alloc = 0x00; - aud_if_cfg.db5_downmix_inh = false; - aud_if_cfg.db5_lsv = 0; - - hdmi_core_audio_infoframe_config(ip_data, &aud_if_cfg); - return 0; + hdmi.ip_data.ops->audio_disable(&hdmi.ip_data); } -static int hdmi_audio_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +int hdmi_audio_start(void) { - if (!hdmi.ip_data.cfg.cm.mode) { - pr_err("Current video settings do not support audio.\n"); - return -EIO; - } - return 0; + DSSDBG("audio_start\n"); + + return hdmi.ip_data.ops->audio_start(&hdmi.ip_data); } -static int hdmi_audio_codec_probe(struct snd_soc_codec *codec) +void hdmi_audio_stop(void) { - struct hdmi_ip_data *priv = &hdmi.ip_data; + DSSDBG("audio_stop\n"); - snd_soc_codec_set_drvdata(codec, priv); - return 0; + hdmi.ip_data.ops->audio_stop(&hdmi.ip_data); } -static struct snd_soc_codec_driver hdmi_audio_codec_drv = { - .probe = hdmi_audio_codec_probe, -}; +bool hdmi_mode_has_audio(void) +{ + if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI) + return true; + else + return false; +} -static struct snd_soc_dai_ops hdmi_audio_codec_ops = { - .hw_params = hdmi_audio_hw_params, - .trigger = hdmi_audio_trigger, - .startup = hdmi_audio_startup, -}; +int hdmi_audio_config(struct omap_dss_audio *audio) +{ + return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio); +} -static struct snd_soc_dai_driver hdmi_codec_dai_drv = { - .name = "hdmi-audio-codec", - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &hdmi_audio_codec_ops, -}; #endif -static int hdmi_get_clocks(struct platform_device *pdev) +static void __init hdmi_probe_pdata(struct platform_device *pdev) { - struct clk *clk; + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int r, i; - clk = clk_get(&pdev->dev, "sys_clk"); - if (IS_ERR(clk)) { - DSSERR("can't get sys_clk\n"); - return PTR_ERR(clk); - } + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; - hdmi.sys_clk = clk; + if (dssdev->type != OMAP_DISPLAY_TYPE_HDMI) + continue; - return 0; -} + r = hdmi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } -static void hdmi_put_clocks(void) -{ - if (hdmi.sys_clk) - clk_put(hdmi.sys_clk); + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } } /* HDMI HW IP initialisation */ -static int omapdss_hdmihw_probe(struct platform_device *pdev) +static int __init omapdss_hdmihw_probe(struct platform_device *pdev) { struct resource *hdmi_mem; int r; - hdmi.pdata = pdev->dev.platform_data; hdmi.pdev = pdev; mutex_init(&hdmi.lock); @@ -830,28 +761,18 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) hdmi_panel_init(); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) + dss_debugfs_create_file("hdmi", hdmi_dump_regs); + + hdmi_probe_pdata(pdev); - /* Register ASoC codec DAI */ - r = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, - &hdmi_codec_dai_drv, 1); - if (r) { - DSSERR("can't register ASoC HDMI audio codec\n"); - return r; - } -#endif return 0; } -static int omapdss_hdmihw_remove(struct platform_device *pdev) +static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) { - hdmi_panel_exit(); + omap_dss_unregister_child_devices(&pdev->dev); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - snd_soc_unregister_codec(&pdev->dev); -#endif + hdmi_panel_exit(); pm_runtime_disable(&pdev->dev); @@ -867,7 +788,6 @@ static int hdmi_runtime_suspend(struct device *dev) clk_disable(hdmi.sys_clk); dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -876,23 +796,13 @@ static int hdmi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; - + return r; clk_enable(hdmi.sys_clk); return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops hdmi_pm_ops = { @@ -901,8 +811,7 @@ static const struct dev_pm_ops hdmi_pm_ops = { }; static struct platform_driver omapdss_hdmihw_driver = { - .probe = omapdss_hdmihw_probe, - .remove = omapdss_hdmihw_remove, + .remove = __exit_p(omapdss_hdmihw_remove), .driver = { .name = "omapdss_hdmi", .owner = THIS_MODULE, @@ -910,12 +819,12 @@ static struct platform_driver omapdss_hdmihw_driver = { }, }; -int hdmi_init_platform_driver(void) +int __init hdmi_init_platform_driver(void) { - return platform_driver_register(&omapdss_hdmihw_driver); + return platform_driver_probe(&omapdss_hdmihw_driver, omapdss_hdmihw_probe); } -void hdmi_uninit_platform_driver(void) +void __exit hdmi_uninit_platform_driver(void) { - return platform_driver_unregister(&omapdss_hdmihw_driver); + platform_driver_unregister(&omapdss_hdmihw_driver); } diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 533d5dc634d2..1179e3c4b1c7 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -30,7 +30,12 @@ #include "dss.h" static struct { - struct mutex hdmi_lock; + /* This protects the panel ops, mainly when accessing the HDMI IP. */ + struct mutex lock; +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + /* This protects the audio ops, specifically. */ + spinlock_t audio_lock; +#endif } hdmi; @@ -54,12 +59,168 @@ static void hdmi_panel_remove(struct omap_dss_device *dssdev) } +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) +{ + unsigned long flags; + int r; + + mutex_lock(&hdmi.lock); + spin_lock_irqsave(&hdmi.audio_lock, flags); + + /* enable audio only if the display is active and supports audio */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || + !hdmi_mode_has_audio()) { + DSSERR("audio not supported or display is off\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_enable(); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + mutex_unlock(&hdmi.lock); + return r; +} + +static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) +{ + unsigned long flags; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + hdmi_audio_disable(); + + dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED; + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); +} + +static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + /* + * No need to check the panel state. It was checked when trasitioning + * to AUDIO_ENABLED. + */ + if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) { + DSSERR("audio start from invalid state\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_start(); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + return r; +} + +static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) +{ + unsigned long flags; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + hdmi_audio_stop(); + dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); +} + +static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) +{ + bool r = false; + + mutex_lock(&hdmi.lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + goto err; + + if (!hdmi_mode_has_audio()) + goto err; + + r = true; +err: + mutex_unlock(&hdmi.lock); + return r; +} + +static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + unsigned long flags; + int r; + + mutex_lock(&hdmi.lock); + spin_lock_irqsave(&hdmi.audio_lock, flags); + + /* config audio only if the display is active and supports audio */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || + !hdmi_mode_has_audio()) { + DSSERR("audio not supported or display is off\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_config(audio); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + mutex_unlock(&hdmi.lock); + return r; +} + +#else +static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) +{ +} + +static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) +{ +} + +static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) +{ + return false; +} + +static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + return -EPERM; +} +#endif + static int hdmi_panel_enable(struct omap_dss_device *dssdev) { int r = 0; DSSDBG("ENTER hdmi_panel_enable\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { r = -EINVAL; @@ -75,40 +236,52 @@ static int hdmi_panel_enable(struct omap_dss_device *dssdev) dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } static void hdmi_panel_disable(struct omap_dss_device *dssdev) { - mutex_lock(&hdmi.hdmi_lock); - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + mutex_lock(&hdmi.lock); + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* + * TODO: notify audio users that the display was disabled. For + * now, disable audio locally to not break our audio state + * machine. + */ + hdmi_panel_audio_disable(dssdev); omapdss_hdmi_display_disable(dssdev); + } dssdev->state = OMAP_DSS_DISPLAY_DISABLED; - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static int hdmi_panel_suspend(struct omap_dss_device *dssdev) { int r = 0; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = -EINVAL; goto err; } - dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + /* + * TODO: notify audio users that the display was suspended. For now, + * disable audio locally to not break our audio state machine. + */ + hdmi_panel_audio_disable(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -117,7 +290,7 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) { int r = 0; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { r = -EINVAL; @@ -129,11 +302,12 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) DSSERR("failed to power on\n"); goto err; } + /* TODO: notify audio users that the panel resumed. */ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -141,11 +315,11 @@ err: static void hdmi_get_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); *timings = dssdev->panel.timings; - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static void hdmi_set_timings(struct omap_dss_device *dssdev, @@ -153,12 +327,18 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev, { DSSDBG("hdmi_set_timings\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); + + /* + * TODO: notify audio users that there was a timings change. For + * now, disable audio locally to not break our audio state machine. + */ + hdmi_panel_audio_disable(dssdev); dssdev->panel.timings = *timings; omapdss_hdmi_display_set_timing(dssdev); - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static int hdmi_check_timings(struct omap_dss_device *dssdev, @@ -168,11 +348,11 @@ static int hdmi_check_timings(struct omap_dss_device *dssdev, DSSDBG("hdmi_check_timings\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); r = omapdss_hdmi_display_check_timing(dssdev, timings); - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -180,7 +360,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) { int r; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = omapdss_hdmi_display_enable(dssdev); @@ -194,7 +374,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -203,7 +383,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) { int r; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = omapdss_hdmi_display_enable(dssdev); @@ -217,7 +397,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -234,6 +414,12 @@ static struct omap_dss_driver hdmi_driver = { .check_timings = hdmi_check_timings, .read_edid = hdmi_read_edid, .detect = hdmi_detect, + .audio_enable = hdmi_panel_audio_enable, + .audio_disable = hdmi_panel_audio_disable, + .audio_start = hdmi_panel_audio_start, + .audio_stop = hdmi_panel_audio_stop, + .audio_supported = hdmi_panel_audio_supported, + .audio_config = hdmi_panel_audio_config, .driver = { .name = "hdmi_panel", .owner = THIS_MODULE, @@ -242,7 +428,11 @@ static struct omap_dss_driver hdmi_driver = { int hdmi_panel_init(void) { - mutex_init(&hdmi.hdmi_lock); + mutex_init(&hdmi.lock); + +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + spin_lock_init(&hdmi.audio_lock); +#endif omap_dss_register_driver(&hdmi_driver); diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index e7364603f6a1..0cbcde4c688a 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -654,9 +654,20 @@ static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr, return 0; } +int dss_mgr_check_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings) +{ + if (!dispc_mgr_timings_ok(mgr->id, timings)) { + DSSERR("check_manager: invalid timings\n"); + return -EINVAL; + } + + return 0; +} + int dss_mgr_check(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, struct omap_overlay_manager_info *info, + const struct omap_video_timings *mgr_timings, struct omap_overlay_info **overlay_infos) { struct omap_overlay *ovl; @@ -668,6 +679,10 @@ int dss_mgr_check(struct omap_overlay_manager *mgr, return r; } + r = dss_mgr_check_timings(mgr, mgr_timings); + if (r) + return r; + list_for_each_entry(ovl, &mgr->overlays, list) { struct omap_overlay_info *oi; int r; @@ -677,7 +692,7 @@ int dss_mgr_check(struct omap_overlay_manager *mgr, if (oi == NULL) continue; - r = dss_ovl_check(ovl, oi, dssdev); + r = dss_ovl_check(ovl, oi, mgr_timings); if (r) return r; } diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 6e821810deec..b0ba60f88dd2 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -628,19 +628,23 @@ int dss_ovl_simple_check(struct omap_overlay *ovl, return -EINVAL; } + if (dss_feat_rotation_type_supported(info->rotation_type) == 0) { + DSSERR("check_overlay: rotation type %d not supported\n", + info->rotation_type); + return -EINVAL; + } + return 0; } -int dss_ovl_check(struct omap_overlay *ovl, - struct omap_overlay_info *info, struct omap_dss_device *dssdev) +int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, + const struct omap_video_timings *mgr_timings) { u16 outw, outh; u16 dw, dh; - if (dssdev == NULL) - return 0; - - dssdev->driver->get_resolution(dssdev, &dw, &dh); + dw = mgr_timings->x_res; + dh = mgr_timings->y_res; if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { outw = info->width; diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 788a0ef6323a..3d8c206e90e5 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -304,13 +304,23 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, u16 height, void (*callback)(void *data), void *data) { u32 l; + struct omap_video_timings timings = { + .hsw = 1, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + .x_res = width, + .y_res = height, + }; /*BUG_ON(callback == 0);*/ BUG_ON(rfbi.framedone_callback != NULL); DSSDBG("rfbi_transfer_area %dx%d\n", width, height); - dispc_mgr_set_lcd_size(dssdev->manager->id, width, height); + dss_mgr_set_timings(dssdev->manager, &timings); dispc_mgr_enable(dssdev->manager->id, true); @@ -766,6 +776,16 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, u16 *x, u16 *y, u16 *w, u16 *h) { u16 dw, dh; + struct omap_video_timings timings = { + .hsw = 1, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + .x_res = *w, + .y_res = *h, + }; dssdev->driver->get_resolution(dssdev, &dw, &dh); @@ -784,7 +804,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, if (*w == 0 || *h == 0) return -EINVAL; - dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h); + dss_mgr_set_timings(dssdev->manager, &timings); return 0; } @@ -799,7 +819,7 @@ int omap_rfbi_update(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omap_rfbi_update); -void rfbi_dump_regs(struct seq_file *s) +static void rfbi_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) @@ -900,15 +920,39 @@ void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL(omapdss_rfbi_display_disable); -int rfbi_init_display(struct omap_dss_device *dssdev) +static int __init rfbi_init_display(struct omap_dss_device *dssdev) { rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev; dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; return 0; } +static void __init rfbi_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DBI) + continue; + + r = rfbi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* RFBI HW IP initialisation */ -static int omap_rfbihw_probe(struct platform_device *pdev) +static int __init omap_rfbihw_probe(struct platform_device *pdev) { u32 rev; struct resource *rfbi_mem; @@ -956,6 +1000,10 @@ static int omap_rfbihw_probe(struct platform_device *pdev) rfbi_runtime_put(); + dss_debugfs_create_file("rfbi", rfbi_dump_regs); + + rfbi_probe_pdata(pdev); + return 0; err_runtime_get: @@ -963,8 +1011,9 @@ err_runtime_get: return r; } -static int omap_rfbihw_remove(struct platform_device *pdev) +static int __exit omap_rfbihw_remove(struct platform_device *pdev) { + omap_dss_unregister_child_devices(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; } @@ -972,7 +1021,6 @@ static int omap_rfbihw_remove(struct platform_device *pdev) static int rfbi_runtime_suspend(struct device *dev) { dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -981,20 +1029,11 @@ static int rfbi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; + return r; return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops rfbi_pm_ops = { @@ -1003,8 +1042,7 @@ static const struct dev_pm_ops rfbi_pm_ops = { }; static struct platform_driver omap_rfbihw_driver = { - .probe = omap_rfbihw_probe, - .remove = omap_rfbihw_remove, + .remove = __exit_p(omap_rfbihw_remove), .driver = { .name = "omapdss_rfbi", .owner = THIS_MODULE, @@ -1012,12 +1050,12 @@ static struct platform_driver omap_rfbihw_driver = { }, }; -int rfbi_init_platform_driver(void) +int __init rfbi_init_platform_driver(void) { - return platform_driver_register(&omap_rfbihw_driver); + return platform_driver_probe(&omap_rfbihw_driver, omap_rfbihw_probe); } -void rfbi_uninit_platform_driver(void) +void __exit rfbi_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_rfbihw_driver); + platform_driver_unregister(&omap_rfbihw_driver); } diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 8266ca0d666b..3a43dc2a9b46 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -24,6 +24,7 @@ #include <linux/err.h> #include <linux/regulator/consumer.h> #include <linux/export.h> +#include <linux/platform_device.h> #include <video/omapdss.h> #include "dss.h" @@ -71,10 +72,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) if (r) goto err_reg_enable; - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) goto err_get_dispc; @@ -107,7 +104,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) } - dispc_mgr_set_lcd_timings(dssdev->manager->id, t); + dss_mgr_set_timings(dssdev->manager, t); r = dss_set_clock_div(&dss_cinfo); if (r) @@ -137,8 +134,6 @@ err_set_dss_clock_div: err_calc_clock_div: dispc_runtime_put(); err_get_dispc: - dss_runtime_put(); -err_get_dss: regulator_disable(sdi.vdds_sdi_reg); err_reg_enable: omap_dss_stop_device(dssdev); @@ -154,7 +149,6 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) dss_sdi_disable(); dispc_runtime_put(); - dss_runtime_put(); regulator_disable(sdi.vdds_sdi_reg); @@ -162,7 +156,7 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL(omapdss_sdi_display_disable); -int sdi_init_display(struct omap_dss_device *dssdev) +static int __init sdi_init_display(struct omap_dss_device *dssdev) { DSSDBG("SDI init\n"); @@ -182,11 +176,58 @@ int sdi_init_display(struct omap_dss_device *dssdev) return 0; } -int sdi_init(void) +static void __init sdi_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_SDI) + continue; + + r = sdi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + +static int __init omap_sdi_probe(struct platform_device *pdev) { + sdi_probe_pdata(pdev); + + return 0; +} + +static int __exit omap_sdi_remove(struct platform_device *pdev) +{ + omap_dss_unregister_child_devices(&pdev->dev); + return 0; } -void sdi_exit(void) +static struct platform_driver omap_sdi_driver = { + .remove = __exit_p(omap_sdi_remove), + .driver = { + .name = "omapdss_sdi", + .owner = THIS_MODULE, + }, +}; + +int __init sdi_init_platform_driver(void) +{ + return platform_driver_probe(&omap_sdi_driver, omap_sdi_probe); +} + +void __exit sdi_uninit_platform_driver(void) { + platform_driver_unregister(&omap_sdi_driver); } diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 1f58b84d6901..e734cb444bc7 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -96,7 +96,9 @@ struct ti_hdmi_ip_ops { void (*pll_disable)(struct hdmi_ip_data *ip_data); - void (*video_enable)(struct hdmi_ip_data *ip_data, bool start); + int (*video_enable)(struct hdmi_ip_data *ip_data); + + void (*video_disable)(struct hdmi_ip_data *ip_data); void (*dump_wrapper)(struct hdmi_ip_data *ip_data, struct seq_file *s); @@ -106,9 +108,17 @@ struct ti_hdmi_ip_ops { void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - void (*audio_enable)(struct hdmi_ip_data *ip_data, bool start); +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + int (*audio_enable)(struct hdmi_ip_data *ip_data); + + void (*audio_disable)(struct hdmi_ip_data *ip_data); + + int (*audio_start)(struct hdmi_ip_data *ip_data); + + void (*audio_stop)(struct hdmi_ip_data *ip_data); + + int (*audio_config)(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio); #endif }; @@ -173,7 +183,8 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data); -void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start); +int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data); @@ -181,8 +192,13 @@ void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable); +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts); +int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio); #endif #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index bfe6fe65c8be..4dae1b291079 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -29,9 +29,14 @@ #include <linux/string.h> #include <linux/seq_file.h> #include <linux/gpio.h> +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +#include <sound/asound.h> +#include <sound/asoundef.h> +#endif #include "ti_hdmi_4xxx_ip.h" #include "dss.h" +#include "dss_features.h" static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, u32 val) @@ -298,9 +303,9 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio), - NULL, hpd_irq_handler, - IRQF_DISABLED | IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, "hpd", ip_data); + NULL, hpd_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "hpd", ip_data); if (r) { DSSERR("HPD IRQ request failed\n"); hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); @@ -699,9 +704,15 @@ static void hdmi_wp_init(struct omap_video_timings *timings, } -void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start) +int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, true, 31, 31); + return 0; +} + +void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data) { - REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, start, 31, 31); + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, false, 31, 31); } static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, @@ -886,10 +897,12 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) #define CORE_REG(i, name) name(i) #define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\ - hdmi_read_reg(hdmi_pll_base(ip_data), r)) -#define DUMPCOREAV(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \ + hdmi_read_reg(hdmi_core_sys_base(ip_data), r)) +#define DUMPCOREAV(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(hdmi_av_base(ip_data), r)) +#define DUMPCOREAV2(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \ (i < 10) ? 32 - strlen(#r) : 31 - strlen(#r), " ", \ - hdmi_read_reg(hdmi_pll_base(ip_data), CORE_REG(i, r))) + hdmi_read_reg(hdmi_av_base(ip_data), CORE_REG(i, r))) DUMPCORE(HDMI_CORE_SYS_VND_IDL); DUMPCORE(HDMI_CORE_SYS_DEV_IDL); @@ -898,6 +911,13 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPCORE(HDMI_CORE_SYS_SRST); DUMPCORE(HDMI_CORE_CTRL1); DUMPCORE(HDMI_CORE_SYS_SYS_STAT); + DUMPCORE(HDMI_CORE_SYS_DE_DLY); + DUMPCORE(HDMI_CORE_SYS_DE_CTRL); + DUMPCORE(HDMI_CORE_SYS_DE_TOP); + DUMPCORE(HDMI_CORE_SYS_DE_CNTL); + DUMPCORE(HDMI_CORE_SYS_DE_CNTH); + DUMPCORE(HDMI_CORE_SYS_DE_LINL); + DUMPCORE(HDMI_CORE_SYS_DE_LINH_1); DUMPCORE(HDMI_CORE_SYS_VID_ACEN); DUMPCORE(HDMI_CORE_SYS_VID_MODE); DUMPCORE(HDMI_CORE_SYS_INTR_STATE); @@ -907,102 +927,91 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPCORE(HDMI_CORE_SYS_INTR4); DUMPCORE(HDMI_CORE_SYS_UMASK1); DUMPCORE(HDMI_CORE_SYS_TMDS_CTRL); - DUMPCORE(HDMI_CORE_SYS_DE_DLY); - DUMPCORE(HDMI_CORE_SYS_DE_CTRL); - DUMPCORE(HDMI_CORE_SYS_DE_TOP); - DUMPCORE(HDMI_CORE_SYS_DE_CNTL); - DUMPCORE(HDMI_CORE_SYS_DE_CNTH); - DUMPCORE(HDMI_CORE_SYS_DE_LINL); - DUMPCORE(HDMI_CORE_SYS_DE_LINH_1); - DUMPCORE(HDMI_CORE_DDC_CMD); - DUMPCORE(HDMI_CORE_DDC_STATUS); DUMPCORE(HDMI_CORE_DDC_ADDR); + DUMPCORE(HDMI_CORE_DDC_SEGM); DUMPCORE(HDMI_CORE_DDC_OFFSET); DUMPCORE(HDMI_CORE_DDC_COUNT1); DUMPCORE(HDMI_CORE_DDC_COUNT2); + DUMPCORE(HDMI_CORE_DDC_STATUS); + DUMPCORE(HDMI_CORE_DDC_CMD); DUMPCORE(HDMI_CORE_DDC_DATA); - DUMPCORE(HDMI_CORE_DDC_SEGM); - DUMPCORE(HDMI_CORE_AV_HDMI_CTRL); - DUMPCORE(HDMI_CORE_AV_DPD); - DUMPCORE(HDMI_CORE_AV_PB_CTRL1); - DUMPCORE(HDMI_CORE_AV_PB_CTRL2); - DUMPCORE(HDMI_CORE_AV_AVI_TYPE); - DUMPCORE(HDMI_CORE_AV_AVI_VERS); - DUMPCORE(HDMI_CORE_AV_AVI_LEN); - DUMPCORE(HDMI_CORE_AV_AVI_CHSUM); + DUMPCOREAV(HDMI_CORE_AV_ACR_CTRL); + DUMPCOREAV(HDMI_CORE_AV_FREQ_SVAL); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL1); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL2); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL3); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL1); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL2); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL3); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL1); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL2); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL3); + DUMPCOREAV(HDMI_CORE_AV_AUD_MODE); + DUMPCOREAV(HDMI_CORE_AV_SPDIF_CTRL); + DUMPCOREAV(HDMI_CORE_AV_HW_SPDIF_FS); + DUMPCOREAV(HDMI_CORE_AV_SWAP_I2S); + DUMPCOREAV(HDMI_CORE_AV_SPDIF_ERTH); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_MAP); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_CTRL); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST0); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST1); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST2); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST4); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST5); + DUMPCOREAV(HDMI_CORE_AV_ASRC); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_LEN); + DUMPCOREAV(HDMI_CORE_AV_HDMI_CTRL); + DUMPCOREAV(HDMI_CORE_AV_AUDO_TXSTAT); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_1); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_2); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_3); + DUMPCOREAV(HDMI_CORE_AV_TEST_TXCTRL); + DUMPCOREAV(HDMI_CORE_AV_DPD); + DUMPCOREAV(HDMI_CORE_AV_PB_CTRL1); + DUMPCOREAV(HDMI_CORE_AV_PB_CTRL2); + DUMPCOREAV(HDMI_CORE_AV_AVI_TYPE); + DUMPCOREAV(HDMI_CORE_AV_AVI_VERS); + DUMPCOREAV(HDMI_CORE_AV_AVI_LEN); + DUMPCOREAV(HDMI_CORE_AV_AVI_CHSUM); for (i = 0; i < HDMI_CORE_AV_AVI_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_AVI_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_AVI_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_SPD_TYPE); + DUMPCOREAV(HDMI_CORE_AV_SPD_VERS); + DUMPCOREAV(HDMI_CORE_AV_SPD_LEN); + DUMPCOREAV(HDMI_CORE_AV_SPD_CHSUM); for (i = 0; i < HDMI_CORE_AV_SPD_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_SPD_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_SPD_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_AUDIO_TYPE); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_VERS); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_LEN); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_CHSUM); for (i = 0; i < HDMI_CORE_AV_AUD_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_AUD_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_AUD_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_MPEG_TYPE); + DUMPCOREAV(HDMI_CORE_AV_MPEG_VERS); + DUMPCOREAV(HDMI_CORE_AV_MPEG_LEN); + DUMPCOREAV(HDMI_CORE_AV_MPEG_CHSUM); for (i = 0; i < HDMI_CORE_AV_MPEG_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_MPEG_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_MPEG_DBYTE); for (i = 0; i < HDMI_CORE_AV_GEN_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_GEN_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_GEN_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_CP_BYTE1); for (i = 0; i < HDMI_CORE_AV_GEN2_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_GEN2_DBYTE); - - DUMPCORE(HDMI_CORE_AV_ACR_CTRL); - DUMPCORE(HDMI_CORE_AV_FREQ_SVAL); - DUMPCORE(HDMI_CORE_AV_N_SVAL1); - DUMPCORE(HDMI_CORE_AV_N_SVAL2); - DUMPCORE(HDMI_CORE_AV_N_SVAL3); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL1); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL2); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL3); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL1); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL2); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL3); - DUMPCORE(HDMI_CORE_AV_AUD_MODE); - DUMPCORE(HDMI_CORE_AV_SPDIF_CTRL); - DUMPCORE(HDMI_CORE_AV_HW_SPDIF_FS); - DUMPCORE(HDMI_CORE_AV_SWAP_I2S); - DUMPCORE(HDMI_CORE_AV_SPDIF_ERTH); - DUMPCORE(HDMI_CORE_AV_I2S_IN_MAP); - DUMPCORE(HDMI_CORE_AV_I2S_IN_CTRL); - DUMPCORE(HDMI_CORE_AV_I2S_CHST0); - DUMPCORE(HDMI_CORE_AV_I2S_CHST1); - DUMPCORE(HDMI_CORE_AV_I2S_CHST2); - DUMPCORE(HDMI_CORE_AV_I2S_CHST4); - DUMPCORE(HDMI_CORE_AV_I2S_CHST5); - DUMPCORE(HDMI_CORE_AV_ASRC); - DUMPCORE(HDMI_CORE_AV_I2S_IN_LEN); - DUMPCORE(HDMI_CORE_AV_HDMI_CTRL); - DUMPCORE(HDMI_CORE_AV_AUDO_TXSTAT); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_1); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_2); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_3); - DUMPCORE(HDMI_CORE_AV_TEST_TXCTRL); - DUMPCORE(HDMI_CORE_AV_DPD); - DUMPCORE(HDMI_CORE_AV_PB_CTRL1); - DUMPCORE(HDMI_CORE_AV_PB_CTRL2); - DUMPCORE(HDMI_CORE_AV_AVI_TYPE); - DUMPCORE(HDMI_CORE_AV_AVI_VERS); - DUMPCORE(HDMI_CORE_AV_AVI_LEN); - DUMPCORE(HDMI_CORE_AV_AVI_CHSUM); - DUMPCORE(HDMI_CORE_AV_SPD_TYPE); - DUMPCORE(HDMI_CORE_AV_SPD_VERS); - DUMPCORE(HDMI_CORE_AV_SPD_LEN); - DUMPCORE(HDMI_CORE_AV_SPD_CHSUM); - DUMPCORE(HDMI_CORE_AV_AUDIO_TYPE); - DUMPCORE(HDMI_CORE_AV_AUDIO_VERS); - DUMPCORE(HDMI_CORE_AV_AUDIO_LEN); - DUMPCORE(HDMI_CORE_AV_AUDIO_CHSUM); - DUMPCORE(HDMI_CORE_AV_MPEG_TYPE); - DUMPCORE(HDMI_CORE_AV_MPEG_VERS); - DUMPCORE(HDMI_CORE_AV_MPEG_LEN); - DUMPCORE(HDMI_CORE_AV_MPEG_CHSUM); - DUMPCORE(HDMI_CORE_AV_CP_BYTE1); - DUMPCORE(HDMI_CORE_AV_CEC_ADDR_ID); + DUMPCOREAV2(i, HDMI_CORE_AV_GEN2_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_CEC_ADDR_ID); } void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) @@ -1016,9 +1025,8 @@ void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); } -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static void ti_hdmi_4xxx_wp_audio_config_format(struct hdmi_ip_data *ip_data, struct hdmi_audio_format *aud_fmt) { u32 r; @@ -1037,7 +1045,7 @@ void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG, r); } -void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_wp_audio_config_dma(struct hdmi_ip_data *ip_data, struct hdmi_audio_dma *aud_dma) { u32 r; @@ -1055,7 +1063,7 @@ void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r); } -void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data, struct hdmi_core_audio_config *cfg) { u32 r; @@ -1106,27 +1114,33 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); - /* I2S parameters */ - REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_CHST4, - cfg->freq_sample, 3, 0); - + /* + * Set IEC-60958-3 channel status word. It is passed to the IP + * just as it is received. The user of the driver is responsible + * for its contents. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST0, + cfg->iec60958_cfg->status[0]); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST1, + cfg->iec60958_cfg->status[1]); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST2, + cfg->iec60958_cfg->status[2]); + /* yes, this is correct: status[3] goes to CHST4 register */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST4, + cfg->iec60958_cfg->status[3]); + /* yes, this is correct: status[4] goes to CHST5 register */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, + cfg->iec60958_cfg->status[4]); + + /* set I2S parameters */ r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL); - r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7); r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); - r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5); r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); - r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3); r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL, r); - r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_CHST5); - r = FLD_MOD(r, cfg->freq_sample, 7, 4); - r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1); - r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0); - hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, r); - REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0); @@ -1138,12 +1152,19 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); r = FLD_MOD(r, cfg->en_spdif, 1, 1); hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r); + + /* Audio channel mappings */ + /* TODO: Make channel mapping dynamic. For now, map channels + * in the ALSA order: FL/FR/RL/RR/C/LFE/SL/SR. Remapping is needed as + * HDMI speaker order is different. See CEA-861 Section 6.6.2. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_MAP, 0x78); + REG_FLD_MOD(av_base, HDMI_CORE_AV_SWAP_I2S, 1, 5, 5); } -void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_audio *info_aud) +static void ti_hdmi_4xxx_core_audio_infoframe_cfg(struct hdmi_ip_data *ip_data, + struct snd_cea_861_aud_if *info_aud) { - u8 val; u8 sum = 0, checksum = 0; void __iomem *av_base = hdmi_av_base(ip_data); @@ -1157,24 +1178,23 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_LEN, 0x0a); sum += 0x84 + 0x001 + 0x00a; - val = (info_aud->db1_coding_type << 4) - | (info_aud->db1_channel_count - 1); - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), + info_aud->db1_ct_cc); + sum += info_aud->db1_ct_cc; - val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), + info_aud->db2_sf_ss); + sum += info_aud->db2_sf_ss; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), 0x00); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), info_aud->db3); + sum += info_aud->db3; - val = info_aud->db4_channel_alloc; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), info_aud->db4_ca); + sum += info_aud->db4_ca; - val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3); - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), + info_aud->db5_dminh_lsv); + sum += info_aud->db5_dminh_lsv; hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(5), 0x00); hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(6), 0x00); @@ -1192,70 +1212,212 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, */ } -int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, - u32 sample_freq, u32 *n, u32 *cts) +int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio) { - u32 r; - u32 deep_color = 0; - u32 pclk = ip_data->cfg.timings.pixel_clock; - - if (n == NULL || cts == NULL) + struct hdmi_audio_format audio_format; + struct hdmi_audio_dma audio_dma; + struct hdmi_core_audio_config core; + int err, n, cts, channel_count; + unsigned int fs_nr; + bool word_length_16b = false; + + if (!audio || !audio->iec || !audio->cea || !ip_data) return -EINVAL; + + core.iec60958_cfg = audio->iec; /* - * Obtain current deep color configuration. This needed - * to calculate the TMDS clock based on the pixel clock. + * In the IEC-60958 status word, check if the audio sample word length + * is 16-bit as several optimizations can be performed in such case. */ - r = REG_GET(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, 1, 0); - switch (r) { - case 1: /* No deep color selected */ - deep_color = 100; + if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24)) + if (audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16) + word_length_16b = true; + + /* I2S configuration. See Phillips' specification */ + if (word_length_16b) + core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; + else + core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + /* + * The I2S input word length is twice the lenght given in the IEC-60958 + * status word. If the word size is greater than + * 20 bits, increment by one. + */ + core.i2s_cfg.in_length_bits = audio->iec->status[4] + & IEC958_AES4_CON_WORDLEN; + if (audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) + core.i2s_cfg.in_length_bits++; + core.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; + core.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; + core.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; + core.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; + + /* convert sample frequency to a number */ + switch (audio->iec->status[3] & IEC958_AES3_CON_FS) { + case IEC958_AES3_CON_FS_32000: + fs_nr = 32000; + break; + case IEC958_AES3_CON_FS_44100: + fs_nr = 44100; + break; + case IEC958_AES3_CON_FS_48000: + fs_nr = 48000; break; - case 2: /* 10-bit deep color selected */ - deep_color = 125; + case IEC958_AES3_CON_FS_88200: + fs_nr = 88200; break; - case 3: /* 12-bit deep color selected */ - deep_color = 150; + case IEC958_AES3_CON_FS_96000: + fs_nr = 96000; + break; + case IEC958_AES3_CON_FS_176400: + fs_nr = 176400; + break; + case IEC958_AES3_CON_FS_192000: + fs_nr = 192000; break; default: return -EINVAL; } - switch (sample_freq) { - case 32000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 4096; + err = hdmi_compute_acr(fs_nr, &n, &cts); + + /* Audio clock regeneration settings */ + core.n = n; + core.cts = cts; + if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { + core.aud_par_busclk = 0; + core.cts_mode = HDMI_AUDIO_CTS_MODE_SW; + core.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); + } else { + core.aud_par_busclk = (((128 * 31) - 1) << 8); + core.cts_mode = HDMI_AUDIO_CTS_MODE_HW; + core.use_mclk = true; + } + + if (core.use_mclk) + core.mclk_mode = HDMI_AUDIO_MCLK_128FS; + + /* Audio channels settings */ + channel_count = (audio->cea->db1_ct_cc & + CEA861_AUDIO_INFOFRAME_DB1CC) + 1; + + switch (channel_count) { + case 2: + audio_format.active_chnnls_msk = 0x03; + break; + case 3: + audio_format.active_chnnls_msk = 0x07; + break; + case 4: + audio_format.active_chnnls_msk = 0x0f; + break; + case 5: + audio_format.active_chnnls_msk = 0x1f; break; - case 44100: - *n = 6272; + case 6: + audio_format.active_chnnls_msk = 0x3f; break; - case 48000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 6144; + case 7: + audio_format.active_chnnls_msk = 0x7f; + break; + case 8: + audio_format.active_chnnls_msk = 0xff; break; default: - *n = 0; return -EINVAL; } - /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ - *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); + /* + * the HDMI IP needs to enable four stereo channels when transmitting + * more than 2 audio channels + */ + if (channel_count == 2) { + audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; + core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; + core.layout = HDMI_AUDIO_LAYOUT_2CH; + } else { + audio_format.stereo_channels = HDMI_AUDIO_STEREO_FOURCHANNELS; + core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN | + HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN | + HDMI_AUDIO_I2S_SD3_EN; + core.layout = HDMI_AUDIO_LAYOUT_8CH; + } + + core.en_spdif = false; + /* use sample frequency from channel status word */ + core.fs_override = true; + /* enable ACR packets */ + core.en_acr_pkt = true; + /* disable direct streaming digital audio */ + core.en_dsd_audio = false; + /* use parallel audio interface */ + core.en_parallel_aud_input = true; + + /* DMA settings */ + if (word_length_16b) + audio_dma.transfer_size = 0x10; + else + audio_dma.transfer_size = 0x20; + audio_dma.block_size = 0xC0; + audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; + audio_dma.fifo_threshold = 0x20; /* in number of samples */ + + /* audio FIFO format settings */ + if (word_length_16b) { + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; + } else { + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + } + audio_format.type = HDMI_AUDIO_TYPE_LPCM; + audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; + /* disable start/stop signals of IEC 60958 blocks */ + audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON; + + /* configure DMA and audio FIFO format*/ + ti_hdmi_4xxx_wp_audio_config_dma(ip_data, &audio_dma); + ti_hdmi_4xxx_wp_audio_config_format(ip_data, &audio_format); + + /* configure the core*/ + ti_hdmi_4xxx_core_audio_config(ip_data, &core); + + /* configure CEA 861 audio infoframe*/ + ti_hdmi_4xxx_core_audio_infoframe_cfg(ip_data, audio->cea); return 0; } -void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable) +int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, true, 31, 31); + return 0; +} + +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, false, 31, 31); +} + +int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data) { REG_FLD_MOD(hdmi_av_base(ip_data), - HDMI_CORE_AV_AUD_MODE, enable, 0, 0); + HDMI_CORE_AV_AUD_MODE, true, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, enable, 31, 31); + HDMI_WP_AUDIO_CTRL, true, 30, 30); + return 0; +} + +void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_av_base(ip_data), + HDMI_CORE_AV_AUD_MODE, false, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, enable, 30, 30); + HDMI_WP_AUDIO_CTRL, false, 30, 30); } #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index a14d1a0e6e41..8366ae19e82e 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -24,11 +24,6 @@ #include <linux/string.h> #include <video/omapdss.h> #include "ti_hdmi.h" -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -#include <sound/soc.h> -#include <sound/pcm_params.h> -#endif /* HDMI Wrapper */ @@ -57,6 +52,13 @@ #define HDMI_CORE_SYS_SRST 0x14 #define HDMI_CORE_CTRL1 0x20 #define HDMI_CORE_SYS_SYS_STAT 0x24 +#define HDMI_CORE_SYS_DE_DLY 0xC8 +#define HDMI_CORE_SYS_DE_CTRL 0xCC +#define HDMI_CORE_SYS_DE_TOP 0xD0 +#define HDMI_CORE_SYS_DE_CNTL 0xD8 +#define HDMI_CORE_SYS_DE_CNTH 0xDC +#define HDMI_CORE_SYS_DE_LINL 0xE0 +#define HDMI_CORE_SYS_DE_LINH_1 0xE4 #define HDMI_CORE_SYS_VID_ACEN 0x124 #define HDMI_CORE_SYS_VID_MODE 0x128 #define HDMI_CORE_SYS_INTR_STATE 0x1C0 @@ -66,50 +68,24 @@ #define HDMI_CORE_SYS_INTR4 0x1D0 #define HDMI_CORE_SYS_UMASK1 0x1D4 #define HDMI_CORE_SYS_TMDS_CTRL 0x208 -#define HDMI_CORE_SYS_DE_DLY 0xC8 -#define HDMI_CORE_SYS_DE_CTRL 0xCC -#define HDMI_CORE_SYS_DE_TOP 0xD0 -#define HDMI_CORE_SYS_DE_CNTL 0xD8 -#define HDMI_CORE_SYS_DE_CNTH 0xDC -#define HDMI_CORE_SYS_DE_LINL 0xE0 -#define HDMI_CORE_SYS_DE_LINH_1 0xE4 + #define HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC 0x1 #define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC 0x1 -#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1 +#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1 #define HDMI_CORE_CTRL1_EDGE_RISINGEDGE 0x1 /* HDMI DDC E-DID */ -#define HDMI_CORE_DDC_CMD 0x3CC -#define HDMI_CORE_DDC_STATUS 0x3C8 #define HDMI_CORE_DDC_ADDR 0x3B4 +#define HDMI_CORE_DDC_SEGM 0x3B8 #define HDMI_CORE_DDC_OFFSET 0x3BC #define HDMI_CORE_DDC_COUNT1 0x3C0 #define HDMI_CORE_DDC_COUNT2 0x3C4 +#define HDMI_CORE_DDC_STATUS 0x3C8 +#define HDMI_CORE_DDC_CMD 0x3CC #define HDMI_CORE_DDC_DATA 0x3D0 -#define HDMI_CORE_DDC_SEGM 0x3B8 /* HDMI IP Core Audio Video */ -#define HDMI_CORE_AV_HDMI_CTRL 0xBC -#define HDMI_CORE_AV_DPD 0xF4 -#define HDMI_CORE_AV_PB_CTRL1 0xF8 -#define HDMI_CORE_AV_PB_CTRL2 0xFC -#define HDMI_CORE_AV_AVI_TYPE 0x100 -#define HDMI_CORE_AV_AVI_VERS 0x104 -#define HDMI_CORE_AV_AVI_LEN 0x108 -#define HDMI_CORE_AV_AVI_CHSUM 0x10C -#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110) -#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15 -#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190) -#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27 -#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210) -#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10 -#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290) -#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27 -#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300) -#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31 -#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380) -#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31 #define HDMI_CORE_AV_ACR_CTRL 0x4 #define HDMI_CORE_AV_FREQ_SVAL 0x8 #define HDMI_CORE_AV_N_SVAL1 0xC @@ -148,25 +124,39 @@ #define HDMI_CORE_AV_AVI_VERS 0x104 #define HDMI_CORE_AV_AVI_LEN 0x108 #define HDMI_CORE_AV_AVI_CHSUM 0x10C +#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110) #define HDMI_CORE_AV_SPD_TYPE 0x180 #define HDMI_CORE_AV_SPD_VERS 0x184 #define HDMI_CORE_AV_SPD_LEN 0x188 #define HDMI_CORE_AV_SPD_CHSUM 0x18C +#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190) #define HDMI_CORE_AV_AUDIO_TYPE 0x200 #define HDMI_CORE_AV_AUDIO_VERS 0x204 #define HDMI_CORE_AV_AUDIO_LEN 0x208 #define HDMI_CORE_AV_AUDIO_CHSUM 0x20C +#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210) #define HDMI_CORE_AV_MPEG_TYPE 0x280 #define HDMI_CORE_AV_MPEG_VERS 0x284 #define HDMI_CORE_AV_MPEG_LEN 0x288 #define HDMI_CORE_AV_MPEG_CHSUM 0x28C +#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290) +#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300) #define HDMI_CORE_AV_CP_BYTE1 0x37C +#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380) #define HDMI_CORE_AV_CEC_ADDR_ID 0x3FC + #define HDMI_CORE_AV_SPD_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_GEN_DBYTE_ELSIZE 0x4 +#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15 +#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27 +#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10 +#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27 +#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31 +#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31 + /* PLL */ #define PLLCTRL_PLL_CONTROL 0x0 @@ -284,35 +274,6 @@ enum hdmi_core_infoframe { HDMI_INFOFRAME_AVI_DB5PR_8 = 7, HDMI_INFOFRAME_AVI_DB5PR_9 = 8, HDMI_INFOFRAME_AVI_DB5PR_10 = 9, - HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB1CT_IEC60958 = 1, - HDMI_INFOFRAME_AUDIO_DB1CT_AC3 = 2, - HDMI_INFOFRAME_AUDIO_DB1CT_MPEG1 = 3, - HDMI_INFOFRAME_AUDIO_DB1CT_MP3 = 4, - HDMI_INFOFRAME_AUDIO_DB1CT_MPEG2_MULTICH = 5, - HDMI_INFOFRAME_AUDIO_DB1CT_AAC = 6, - HDMI_INFOFRAME_AUDIO_DB1CT_DTS = 7, - HDMI_INFOFRAME_AUDIO_DB1CT_ATRAC = 8, - HDMI_INFOFRAME_AUDIO_DB1CT_ONEBIT = 9, - HDMI_INFOFRAME_AUDIO_DB1CT_DOLBY_DIGITAL_PLUS = 10, - HDMI_INFOFRAME_AUDIO_DB1CT_DTS_HD = 11, - HDMI_INFOFRAME_AUDIO_DB1CT_MAT = 12, - HDMI_INFOFRAME_AUDIO_DB1CT_DST = 13, - HDMI_INFOFRAME_AUDIO_DB1CT_WMA_PRO = 14, - HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB2SF_32000 = 1, - HDMI_INFOFRAME_AUDIO_DB2SF_44100 = 2, - HDMI_INFOFRAME_AUDIO_DB2SF_48000 = 3, - HDMI_INFOFRAME_AUDIO_DB2SF_88200 = 4, - HDMI_INFOFRAME_AUDIO_DB2SF_96000 = 5, - HDMI_INFOFRAME_AUDIO_DB2SF_176400 = 6, - HDMI_INFOFRAME_AUDIO_DB2SF_192000 = 7, - HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB2SS_16BIT = 1, - HDMI_INFOFRAME_AUDIO_DB2SS_20BIT = 2, - HDMI_INFOFRAME_AUDIO_DB2SS_24BIT = 3, - HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PERMITTED = 0, - HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PROHIBITED = 1 }; enum hdmi_packing_mode { @@ -322,17 +283,6 @@ enum hdmi_packing_mode { HDMI_PACK_ALREADYPACKED = 7 }; -enum hdmi_core_audio_sample_freq { - HDMI_AUDIO_FS_32000 = 0x3, - HDMI_AUDIO_FS_44100 = 0x0, - HDMI_AUDIO_FS_48000 = 0x2, - HDMI_AUDIO_FS_88200 = 0x8, - HDMI_AUDIO_FS_96000 = 0xA, - HDMI_AUDIO_FS_176400 = 0xC, - HDMI_AUDIO_FS_192000 = 0xE, - HDMI_AUDIO_FS_NOT_INDICATED = 0x1 -}; - enum hdmi_core_audio_layout { HDMI_AUDIO_LAYOUT_2CH = 0, HDMI_AUDIO_LAYOUT_8CH = 1 @@ -387,37 +337,12 @@ enum hdmi_audio_blk_strt_end_sig { }; enum hdmi_audio_i2s_config { - HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT = 0, - HDMI_AUDIO_I2S_WS_POLARIT_YLOW_IS_RIGHT = 1, HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0, HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1, - HDMI_AUDIO_I2S_MAX_WORD_20BITS = 0, - HDMI_AUDIO_I2S_MAX_WORD_24BITS = 1, - HDMI_AUDIO_I2S_CHST_WORD_NOT_SPECIFIED = 0, - HDMI_AUDIO_I2S_CHST_WORD_16_BITS = 1, - HDMI_AUDIO_I2S_CHST_WORD_17_BITS = 6, - HDMI_AUDIO_I2S_CHST_WORD_18_BITS = 2, - HDMI_AUDIO_I2S_CHST_WORD_19_BITS = 4, - HDMI_AUDIO_I2S_CHST_WORD_20_BITS_20MAX = 5, - HDMI_AUDIO_I2S_CHST_WORD_20_BITS_24MAX = 1, - HDMI_AUDIO_I2S_CHST_WORD_21_BITS = 6, - HDMI_AUDIO_I2S_CHST_WORD_22_BITS = 2, - HDMI_AUDIO_I2S_CHST_WORD_23_BITS = 4, - HDMI_AUDIO_I2S_CHST_WORD_24_BITS = 5, HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0, HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1, HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0, HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1, - HDMI_AUDIO_I2S_INPUT_LENGTH_NA = 0, - HDMI_AUDIO_I2S_INPUT_LENGTH_16 = 2, - HDMI_AUDIO_I2S_INPUT_LENGTH_17 = 12, - HDMI_AUDIO_I2S_INPUT_LENGTH_18 = 4, - HDMI_AUDIO_I2S_INPUT_LENGTH_19 = 8, - HDMI_AUDIO_I2S_INPUT_LENGTH_20 = 10, - HDMI_AUDIO_I2S_INPUT_LENGTH_21 = 13, - HDMI_AUDIO_I2S_INPUT_LENGTH_22 = 5, - HDMI_AUDIO_I2S_INPUT_LENGTH_23 = 9, - HDMI_AUDIO_I2S_INPUT_LENGTH_24 = 11, HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0, HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1, HDMI_AUDIO_I2S_SD0_EN = 1, @@ -446,20 +371,6 @@ struct hdmi_core_video_config { enum hdmi_core_tclkselclkmult tclk_sel_clkmult; }; -/* - * Refer to section 8.2 in HDMI 1.3 specification for - * details about infoframe databytes - */ -struct hdmi_core_infoframe_audio { - u8 db1_coding_type; - u8 db1_channel_count; - u8 db2_sample_freq; - u8 db2_sample_size; - u8 db4_channel_alloc; - bool db5_downmix_inh; - u8 db5_lsv; /* Level shift values for downmix */ -}; - struct hdmi_core_packet_enable_repeat { u32 audio_pkt; u32 audio_pkt_repeat; @@ -496,15 +407,10 @@ struct hdmi_audio_dma { }; struct hdmi_core_audio_i2s_config { - u8 word_max_length; - u8 word_length; u8 in_length_bits; u8 justification; - u8 en_high_bitrate_aud; u8 sck_edge_mode; - u8 cbit_order; u8 vbit; - u8 ws_polarity; u8 direction; u8 shift; u8 active_sds; @@ -512,7 +418,7 @@ struct hdmi_core_audio_i2s_config { struct hdmi_core_audio_config { struct hdmi_core_audio_i2s_config i2s_cfg; - enum hdmi_core_audio_sample_freq freq_sample; + struct snd_aes_iec958 *iec60958_cfg; bool fs_override; u32 n; u32 cts; @@ -527,17 +433,4 @@ struct hdmi_core_audio_config { bool en_spdif; }; -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, - u32 sample_freq, u32 *n, u32 *cts); -void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_audio *info_aud); -void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_audio_config *cfg); -void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, - struct hdmi_audio_dma *aud_dma); -void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, - struct hdmi_audio_format *aud_fmt); -#endif #endif diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 9c3daf71750c..2b8973931ff4 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -415,6 +415,7 @@ static const struct venc_config *venc_timings_to_config( return &venc_config_ntsc_trm; BUG(); + return NULL; } static int venc_power_on(struct omap_dss_device *dssdev) @@ -440,10 +441,11 @@ static int venc_power_on(struct omap_dss_device *dssdev) venc_write_reg(VENC_OUTPUT_CONTROL, l); - dispc_set_digit_size(dssdev->panel.timings.x_res, - dssdev->panel.timings.y_res/2); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); - regulator_enable(venc.vdda_dac_reg); + r = regulator_enable(venc.vdda_dac_reg); + if (r) + goto err; if (dssdev->platform_enable) dssdev->platform_enable(dssdev); @@ -485,16 +487,68 @@ unsigned long venc_get_pixel_clock(void) return 13500000; } +static ssize_t display_output_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + const char *ret; + + switch (dssdev->phy.venc.type) { + case OMAP_DSS_VENC_TYPE_COMPOSITE: + ret = "composite"; + break; + case OMAP_DSS_VENC_TYPE_SVIDEO: + ret = "svideo"; + break; + default: + return -EINVAL; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", ret); +} + +static ssize_t display_output_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + enum omap_dss_venc_type new_type; + + if (sysfs_streq("composite", buf)) + new_type = OMAP_DSS_VENC_TYPE_COMPOSITE; + else if (sysfs_streq("svideo", buf)) + new_type = OMAP_DSS_VENC_TYPE_SVIDEO; + else + return -EINVAL; + + mutex_lock(&venc.venc_lock); + + if (dssdev->phy.venc.type != new_type) { + dssdev->phy.venc.type = new_type; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + venc_power_off(dssdev); + venc_power_on(dssdev); + } + } + + mutex_unlock(&venc.venc_lock); + + return size; +} + +static DEVICE_ATTR(output_type, S_IRUGO | S_IWUSR, + display_output_type_show, display_output_type_store); + /* driver */ static int venc_panel_probe(struct omap_dss_device *dssdev) { dssdev->panel.timings = omap_dss_pal_timings; - return 0; + return device_create_file(&dssdev->dev, &dev_attr_output_type); } static void venc_panel_remove(struct omap_dss_device *dssdev) { + device_remove_file(&dssdev->dev, &dev_attr_output_type); } static int venc_panel_enable(struct omap_dss_device *dssdev) @@ -577,12 +631,6 @@ static int venc_panel_resume(struct omap_dss_device *dssdev) return venc_panel_enable(dssdev); } -static void venc_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void venc_set_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -597,6 +645,8 @@ static void venc_set_timings(struct omap_dss_device *dssdev, /* turn the venc off and on to get new timings to use */ venc_panel_disable(dssdev); venc_panel_enable(dssdev); + } else { + dss_mgr_set_timings(dssdev->manager, timings); } } @@ -661,7 +711,6 @@ static struct omap_dss_driver venc_driver = { .get_resolution = omapdss_default_get_resolution, .get_recommended_bpp = omapdss_default_get_recommended_bpp, - .get_timings = venc_get_timings, .set_timings = venc_set_timings, .check_timings = venc_check_timings, @@ -675,7 +724,7 @@ static struct omap_dss_driver venc_driver = { }; /* driver end */ -int venc_init_display(struct omap_dss_device *dssdev) +static int __init venc_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -695,7 +744,7 @@ int venc_init_display(struct omap_dss_device *dssdev) return 0; } -void venc_dump_regs(struct seq_file *s) +static void venc_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) @@ -779,8 +828,32 @@ static void venc_put_clocks(void) clk_put(venc.tv_dac_clk); } +static void __init venc_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int r, i; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) + continue; + + r = venc_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* VENC HW IP initialisation */ -static int omap_venchw_probe(struct platform_device *pdev) +static int __init omap_venchw_probe(struct platform_device *pdev) { u8 rev_id; struct resource *venc_mem; @@ -824,6 +897,10 @@ static int omap_venchw_probe(struct platform_device *pdev) if (r) goto err_reg_panel_driver; + dss_debugfs_create_file("venc", venc_dump_regs); + + venc_probe_pdata(pdev); + return 0; err_reg_panel_driver: @@ -833,12 +910,15 @@ err_runtime_get: return r; } -static int omap_venchw_remove(struct platform_device *pdev) +static int __exit omap_venchw_remove(struct platform_device *pdev) { + omap_dss_unregister_child_devices(&pdev->dev); + if (venc.vdda_dac_reg != NULL) { regulator_put(venc.vdda_dac_reg); venc.vdda_dac_reg = NULL; } + omap_dss_unregister_driver(&venc_driver); pm_runtime_disable(&pdev->dev); @@ -853,7 +933,6 @@ static int venc_runtime_suspend(struct device *dev) clk_disable(venc.tv_dac_clk); dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -862,23 +941,14 @@ static int venc_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; + return r; if (venc.tv_dac_clk) clk_enable(venc.tv_dac_clk); return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops venc_pm_ops = { @@ -887,8 +957,7 @@ static const struct dev_pm_ops venc_pm_ops = { }; static struct platform_driver omap_venchw_driver = { - .probe = omap_venchw_probe, - .remove = omap_venchw_remove, + .remove = __exit_p(omap_venchw_remove), .driver = { .name = "omapdss_venc", .owner = THIS_MODULE, @@ -896,18 +965,18 @@ static struct platform_driver omap_venchw_driver = { }, }; -int venc_init_platform_driver(void) +int __init venc_init_platform_driver(void) { if (cpu_is_omap44xx()) return 0; - return platform_driver_register(&omap_venchw_driver); + return platform_driver_probe(&omap_venchw_driver, omap_venchw_probe); } -void venc_uninit_platform_driver(void) +void __exit venc_uninit_platform_driver(void) { if (cpu_is_omap44xx()) return; - return platform_driver_unregister(&omap_venchw_driver); + platform_driver_unregister(&omap_venchw_driver); } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 6a09ef87e14f..c6cf372d22c5 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -70,7 +70,7 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) DBG("omapfb_setup_plane\n"); - if (ofbi->num_overlays != 1) { + if (ofbi->num_overlays == 0) { r = -EINVAL; goto out; } @@ -185,7 +185,7 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) { struct omapfb_info *ofbi = FB2OFB(fbi); - if (ofbi->num_overlays != 1) { + if (ofbi->num_overlays == 0) { memset(pi, 0, sizeof(*pi)); } else { struct omap_overlay *ovl; @@ -225,6 +225,9 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) down_write_nested(&rg->lock, rg->id); atomic_inc(&rg->lock_count); + if (rg->size == size && rg->type == mi->type) + goto out; + if (atomic_read(&rg->map_count)) { r = -EBUSY; goto out; @@ -247,12 +250,10 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) } } - if (rg->size != size || rg->type != mi->type) { - r = omapfb_realloc_fbmem(fbi, size, mi->type); - if (r) { - dev_err(fbdev->dev, "realloc fbmem failed\n"); - goto out; - } + r = omapfb_realloc_fbmem(fbi, size, mi->type); + if (r) { + dev_err(fbdev->dev, "realloc fbmem failed\n"); + goto out; } out: diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index b00db4068d21..3450ea0966c9 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -179,6 +179,7 @@ static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) break; default: BUG(); + return 0; } offset *= vrfb->bytespp; @@ -1502,7 +1503,7 @@ static int omapfb_parse_vram_param(const char *param, int max_entries, fbnum = simple_strtoul(p, &p, 10); - if (p == param) + if (p == start) return -EINVAL; if (*p != ':') @@ -2307,7 +2308,7 @@ static int omapfb_init_display(struct omapfb2_device *fbdev, return 0; } -static int omapfb_probe(struct platform_device *pdev) +static int __init omapfb_probe(struct platform_device *pdev) { struct omapfb2_device *fbdev = NULL; int r = 0; @@ -2448,7 +2449,7 @@ err0: return r; } -static int omapfb_remove(struct platform_device *pdev) +static int __exit omapfb_remove(struct platform_device *pdev) { struct omapfb2_device *fbdev = platform_get_drvdata(pdev); @@ -2462,8 +2463,7 @@ static int omapfb_remove(struct platform_device *pdev) } static struct platform_driver omapfb_driver = { - .probe = omapfb_probe, - .remove = omapfb_remove, + .remove = __exit_p(omapfb_remove), .driver = { .name = "omapfb", .owner = THIS_MODULE, @@ -2474,7 +2474,7 @@ static int __init omapfb_init(void) { DBG("omapfb_init\n"); - if (platform_driver_register(&omapfb_driver)) { + if (platform_driver_probe(&omapfb_driver, omapfb_probe)) { printk(KERN_ERR "failed to register omapfb driver\n"); return -ENODEV; } diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h index c0bdc9b54ecf..30361a09aecd 100644 --- a/drivers/video/omap2/omapfb/omapfb.h +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -166,6 +166,7 @@ static inline struct omapfb_display_data *get_display_data( /* This should never happen */ BUG(); + return NULL; } static inline void omapfb_lock(struct omapfb2_device *fbdev) diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c index 4e5b960c32c8..7e990220ad2a 100644 --- a/drivers/video/omap2/vrfb.c +++ b/drivers/video/omap2/vrfb.c @@ -179,8 +179,10 @@ void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr, pixel_size_exp = 2; else if (bytespp == 2) pixel_size_exp = 1; - else + else { BUG(); + return; + } vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp; vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT); diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c index 1d71c08a818f..0b4ae0cebeda 100644 --- a/drivers/video/pxa3xx-gcu.c +++ b/drivers/video/pxa3xx-gcu.c @@ -316,12 +316,9 @@ pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv) ret = wait_event_interruptible_timeout(priv->wait_idle, !priv->shared->hw_running, HZ*4); - if (ret < 0) + if (ret != 0) break; - if (ret > 0) - continue; - if (gc_readl(priv, REG_GCRBEXHR) == rbexhr && priv->shared->num_interrupts == num) { QERROR("TIMEOUT"); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index f3105160bf98..5f9d8e69029e 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -47,7 +47,7 @@ #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE #undef writel #define writel(v, r) do { \ - printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \ + pr_debug("%s: %08x => %p\n", __func__, (unsigned int)v, r); \ __raw_writel(v, r); \ } while (0) #endif /* FB_S3C_DEBUG_REGWRITE */ @@ -495,7 +495,6 @@ static int s3c_fb_set_par(struct fb_info *info) u32 alpha = 0; u32 data; u32 pagewidth; - int clkdiv; dev_dbg(sfb->dev, "setting framebuffer parameters\n"); @@ -532,48 +531,9 @@ static int s3c_fb_set_par(struct fb_info *info) /* disable the window whilst we update it */ writel(0, regs + WINCON(win_no)); - /* use platform specified window as the basis for the lcd timings */ - - if (win_no == sfb->pdata->default_win) { - clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); - - data = sfb->pdata->vidcon0; - data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); - - if (clkdiv > 1) - data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; - else - data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ - - /* write the timing data to the panel */ - - if (sfb->variant.is_2443) - data |= (1 << 5); - - writel(data, regs + VIDCON0); - + if (!sfb->output_on) s3c_fb_enable(sfb, 1); - data = VIDTCON0_VBPD(var->upper_margin - 1) | - VIDTCON0_VFPD(var->lower_margin - 1) | - VIDTCON0_VSPW(var->vsync_len - 1); - - writel(data, regs + sfb->variant.vidtcon); - - data = VIDTCON1_HBPD(var->left_margin - 1) | - VIDTCON1_HFPD(var->right_margin - 1) | - VIDTCON1_HSPW(var->hsync_len - 1); - - /* VIDTCON1 */ - writel(data, regs + sfb->variant.vidtcon + 4); - - data = VIDTCON2_LINEVAL(var->yres - 1) | - VIDTCON2_HOZVAL(var->xres - 1) | - VIDTCON2_LINEVAL_E(var->yres - 1) | - VIDTCON2_HOZVAL_E(var->xres - 1); - writel(data, regs + sfb->variant.vidtcon + 8); - } - /* write the buffer address */ /* start and end registers stride is 8 */ @@ -839,6 +799,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) struct s3c_fb *sfb = win->parent; unsigned int index = win->index; u32 wincon; + u32 output_on = sfb->output_on; dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); @@ -877,34 +838,18 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) shadow_protect_win(win, 1); writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4)); - shadow_protect_win(win, 0); /* Check the enabled state to see if we need to be running the * main LCD interface, as if there are no active windows then * it is highly likely that we also do not need to output * anything. */ - - /* We could do something like the following code, but the current - * system of using framebuffer events means that we cannot make - * the distinction between just window 0 being inactive and all - * the windows being down. - * - * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); - */ - - /* we're stuck with this until we can do something about overriding - * the power control using the blanking event for a single fb. - */ - if (index == sfb->pdata->default_win) { - shadow_protect_win(win, 1); - s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); - shadow_protect_win(win, 0); - } + s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); + shadow_protect_win(win, 0); pm_runtime_put_sync(sfb->dev); - return 0; + return output_on == sfb->output_on; } /** @@ -1111,7 +1056,7 @@ static struct fb_ops s3c_fb_ops = { * * Calculate the pixel clock when none has been given through platform data. */ -static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode) +static void s3c_fb_missing_pixclock(struct fb_videomode *mode) { u64 pixclk = 1000000000000ULL; u32 div; @@ -1144,11 +1089,11 @@ static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb, dev_dbg(sfb->dev, "allocating memory for display\n"); - real_size = windata->win_mode.xres * windata->win_mode.yres; + real_size = windata->xres * windata->yres; virt_size = windata->virtual_x * windata->virtual_y; dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n", - real_size, windata->win_mode.xres, windata->win_mode.yres, + real_size, windata->xres, windata->yres, virt_size, windata->virtual_x, windata->virtual_y); size = (real_size > virt_size) ? real_size : virt_size; @@ -1230,7 +1175,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, struct s3c_fb_win **res) { struct fb_var_screeninfo *var; - struct fb_videomode *initmode; + struct fb_videomode initmode; struct s3c_fb_pd_win *windata; struct s3c_fb_win *win; struct fb_info *fbinfo; @@ -1251,11 +1196,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } windata = sfb->pdata->win[win_no]; - initmode = &windata->win_mode; + initmode = *sfb->pdata->vtiming; WARN_ON(windata->max_bpp == 0); - WARN_ON(windata->win_mode.xres == 0); - WARN_ON(windata->win_mode.yres == 0); + WARN_ON(windata->xres == 0); + WARN_ON(windata->yres == 0); win = fbinfo->par; *res = win; @@ -1294,7 +1239,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /* setup the initial video mode from the window */ - fb_videomode_to_var(&fbinfo->var, initmode); + initmode.xres = windata->xres; + initmode.yres = windata->yres; + fb_videomode_to_var(&fbinfo->var, &initmode); fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; fbinfo->fix.accel = FB_ACCEL_NONE; @@ -1339,6 +1286,53 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /** + * s3c_fb_set_rgb_timing() - set video timing for rgb interface. + * @sfb: The base resources for the hardware. + * + * Set horizontal and vertical lcd rgb interface timing. + */ +static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb) +{ + struct fb_videomode *vmode = sfb->pdata->vtiming; + void __iomem *regs = sfb->regs; + int clkdiv; + u32 data; + + if (!vmode->pixclock) + s3c_fb_missing_pixclock(vmode); + + clkdiv = s3c_fb_calc_pixclk(sfb, vmode->pixclock); + + data = sfb->pdata->vidcon0; + data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); + + if (clkdiv > 1) + data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; + else + data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ + + if (sfb->variant.is_2443) + data |= (1 << 5); + writel(data, regs + VIDCON0); + + data = VIDTCON0_VBPD(vmode->upper_margin - 1) | + VIDTCON0_VFPD(vmode->lower_margin - 1) | + VIDTCON0_VSPW(vmode->vsync_len - 1); + writel(data, regs + sfb->variant.vidtcon); + + data = VIDTCON1_HBPD(vmode->left_margin - 1) | + VIDTCON1_HFPD(vmode->right_margin - 1) | + VIDTCON1_HSPW(vmode->hsync_len - 1); + writel(data, regs + sfb->variant.vidtcon + 4); + + data = VIDTCON2_LINEVAL(vmode->yres - 1) | + VIDTCON2_HOZVAL(vmode->xres - 1) | + VIDTCON2_LINEVAL_E(vmode->yres - 1) | + VIDTCON2_HOZVAL_E(vmode->xres - 1); + writel(data, regs + sfb->variant.vidtcon + 8); +} + +/** * s3c_fb_clear_win() - clear hardware window registers. * @sfb: The base resources for the hardware. * @win: The window to process. @@ -1481,15 +1475,14 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) writel(0xffffff, regs + WKEYCON1); } + s3c_fb_set_rgb_timing(sfb); + /* we have the register setup, start allocating framebuffers */ for (win = 0; win < fbdrv->variant.nr_windows; win++) { if (!pd->win[win]) continue; - if (!pd->win[win]->win_mode.pixclock) - s3c_fb_missing_pixclock(&pd->win[win]->win_mode); - ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win], &sfb->windows[win]); if (ret < 0) { @@ -1564,6 +1557,8 @@ static int s3c_fb_suspend(struct device *dev) struct s3c_fb_win *win; int win_no; + pm_runtime_get_sync(sfb->dev); + for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) { win = sfb->windows[win_no]; if (!win) @@ -1577,6 +1572,9 @@ static int s3c_fb_suspend(struct device *dev) clk_disable(sfb->lcd_clk); clk_disable(sfb->bus_clk); + + pm_runtime_put_sync(sfb->dev); + return 0; } @@ -1589,6 +1587,8 @@ static int s3c_fb_resume(struct device *dev) int win_no; u32 reg; + pm_runtime_get_sync(sfb->dev); + clk_enable(sfb->bus_clk); if (!sfb->variant.has_clksel) @@ -1623,6 +1623,8 @@ static int s3c_fb_resume(struct device *dev) shadow_protect_win(win, 0); } + s3c_fb_set_rgb_timing(sfb); + /* restore framebuffers */ for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { win = sfb->windows[win_no]; @@ -1633,6 +1635,8 @@ static int s3c_fb_resume(struct device *dev) s3c_fb_set_par(win->fbinfo); } + pm_runtime_put_sync(sfb->dev); + return 0; } #endif diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index eafb19da2c07..930e550e752a 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -31,6 +31,7 @@ #include "sh_mobile_lcdcfb.h" +/* HDMI Core Control Register (HTOP0) */ #define HDMI_SYSTEM_CTRL 0x00 /* System control */ #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ @@ -201,6 +202,68 @@ #define HDMI_REVISION_ID 0xF1 /* Revision ID */ #define HDMI_TEST_MODE 0xFE /* Test mode */ +/* HDMI Control Register (HTOP1) */ +#define HDMI_HTOP1_TEST_MODE 0x0000 /* Test mode */ +#define HDMI_HTOP1_VIDEO_INPUT 0x0008 /* VideoInput */ +#define HDMI_HTOP1_CORE_RSTN 0x000C /* CoreResetn */ +#define HDMI_HTOP1_PLLBW 0x0018 /* PLLBW */ +#define HDMI_HTOP1_CLK_TO_PHY 0x001C /* Clk to Phy */ +#define HDMI_HTOP1_VIDEO_INPUT2 0x0020 /* VideoInput2 */ +#define HDMI_HTOP1_TISEMP0_1 0x0024 /* tisemp0-1 */ +#define HDMI_HTOP1_TISEMP2_C 0x0028 /* tisemp2-c */ +#define HDMI_HTOP1_TISIDRV 0x002C /* tisidrv */ +#define HDMI_HTOP1_TISEN 0x0034 /* tisen */ +#define HDMI_HTOP1_TISDREN 0x0038 /* tisdren */ +#define HDMI_HTOP1_CISRANGE 0x003C /* cisrange */ +#define HDMI_HTOP1_ENABLE_SELECTOR 0x0040 /* Enable Selector */ +#define HDMI_HTOP1_MACRO_RESET 0x0044 /* Macro reset */ +#define HDMI_HTOP1_PLL_CALIBRATION 0x0048 /* PLL calibration */ +#define HDMI_HTOP1_RE_CALIBRATION 0x004C /* Re-calibration */ +#define HDMI_HTOP1_CURRENT 0x0050 /* Current */ +#define HDMI_HTOP1_PLL_LOCK_DETECT 0x0054 /* PLL lock detect */ +#define HDMI_HTOP1_PHY_TEST_MODE 0x0058 /* PHY Test Mode */ +#define HDMI_HTOP1_CLK_SET 0x0080 /* Clock Set */ +#define HDMI_HTOP1_DDC_FAIL_SAFE 0x0084 /* DDC fail safe */ +#define HDMI_HTOP1_PRBS 0x0088 /* PRBS */ +#define HDMI_HTOP1_EDID_AINC_CONTROL 0x008C /* EDID ainc Control */ +#define HDMI_HTOP1_HTOP_DCL_MODE 0x00FC /* Deep Coloer Mode */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF0 0x0100 /* Deep Color:FRC COEF0 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF1 0x0104 /* Deep Color:FRC COEF1 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF2 0x0108 /* Deep Color:FRC COEF2 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF3 0x010C /* Deep Color:FRC COEF3 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF0_C 0x0110 /* Deep Color:FRC COEF0C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF1_C 0x0114 /* Deep Color:FRC COEF1C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF2_C 0x0118 /* Deep Color:FRC COEF2C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF3_C 0x011C /* Deep Color:FRC COEF3C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_MODE 0x0120 /* Deep Color:FRC Mode */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START1 0x0124 /* Deep Color:Rect Start1 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE1 0x0128 /* Deep Color:Rect Size1 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START2 0x012C /* Deep Color:Rect Start2 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE2 0x0130 /* Deep Color:Rect Size2 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START3 0x0134 /* Deep Color:Rect Start3 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE3 0x0138 /* Deep Color:Rect Size3 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START4 0x013C /* Deep Color:Rect Start4 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE4 0x0140 /* Deep Color:Rect Size4 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_1 0x0144 /* Deep Color:Fil Para Y1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_2 0x0148 /* Deep Color:Fil Para Y1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_1 0x014C /* Deep Color:Fil Para CB1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_2 0x0150 /* Deep Color:Fil Para CB1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_1 0x0154 /* Deep Color:Fil Para CR1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_2 0x0158 /* Deep Color:Fil Para CR1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_1 0x015C /* Deep Color:Fil Para Y2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_2 0x0160 /* Deep Color:Fil Para Y2_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_1 0x0164 /* Deep Color:Fil Para CB2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_2 0x0168 /* Deep Color:Fil Para CB2_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_1 0x016C /* Deep Color:Fil Para CR2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_2 0x0170 /* Deep Color:Fil Para CR2_2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_Y1 0x0174 /* Deep Color:Cor Para Y1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CB1 0x0178 /* Deep Color:Cor Para CB1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CR1 0x017C /* Deep Color:Cor Para CR1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_Y2 0x0180 /* Deep Color:Cor Para Y2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CB2 0x0184 /* Deep Color:Cor Para CB2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CR2 0x0188 /* Deep Color:Cor Para CR2 */ +#define HDMI_HTOP1_EDID_DATA_READ 0x0200 /* EDID Data Read 128Byte:0x03FC */ + enum hotplug_state { HDMI_HOTPLUG_DISCONNECTED, HDMI_HOTPLUG_CONNECTED, @@ -211,6 +274,7 @@ struct sh_hdmi { struct sh_mobile_lcdc_entity entity; void __iomem *base; + void __iomem *htop1; enum hotplug_state hp_state; /* hot-plug status */ u8 preprogrammed_vic; /* use a pre-programmed VIC or the external mode */ @@ -222,20 +286,66 @@ struct sh_hdmi { struct delayed_work edid_work; struct fb_videomode mode; struct fb_monspecs monspec; + + /* register access functions */ + void (*write)(struct sh_hdmi *hdmi, u8 data, u8 reg); + u8 (*read)(struct sh_hdmi *hdmi, u8 reg); }; #define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity) -static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) +static void __hdmi_write8(struct sh_hdmi *hdmi, u8 data, u8 reg) { iowrite8(data, hdmi->base + reg); } -static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) +static u8 __hdmi_read8(struct sh_hdmi *hdmi, u8 reg) { return ioread8(hdmi->base + reg); } +static void __hdmi_write32(struct sh_hdmi *hdmi, u8 data, u8 reg) +{ + iowrite32((u32)data, hdmi->base + (reg * 4)); + udelay(100); +} + +static u8 __hdmi_read32(struct sh_hdmi *hdmi, u8 reg) +{ + return (u8)ioread32(hdmi->base + (reg * 4)); +} + +static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) +{ + hdmi->write(hdmi, data, reg); +} + +static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) +{ + return hdmi->read(hdmi, reg); +} + +static void hdmi_bit_set(struct sh_hdmi *hdmi, u8 mask, u8 data, u8 reg) +{ + u8 val = hdmi_read(hdmi, reg); + + val &= ~mask; + val |= (data & mask); + + hdmi_write(hdmi, val, reg); +} + +static void hdmi_htop1_write(struct sh_hdmi *hdmi, u32 data, u32 reg) +{ + iowrite32(data, hdmi->htop1 + reg); + udelay(100); +} + +static u32 hdmi_htop1_read(struct sh_hdmi *hdmi, u32 reg) +{ + return ioread32(hdmi->htop1 + reg); +} + /* * HDMI sound */ @@ -693,11 +803,11 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) msleep(10); /* PS mode b->d, reset PLLA and PLLB */ - hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x4C, HDMI_SYSTEM_CTRL); udelay(10); - hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x40, HDMI_SYSTEM_CTRL); } static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, @@ -746,7 +856,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, /* Read EDID */ dev_dbg(hdmi->dev, "Read back EDID code:"); for (i = 0; i < 128; i++) { - edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); + edid[i] = (hdmi->htop1) ? + (u8)hdmi_htop1_read(hdmi, HDMI_HTOP1_EDID_DATA_READ + (i * 4)) : + hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); #ifdef DEBUG if ((i % 16) == 0) { printk(KERN_CONT "\n"); @@ -917,13 +1029,13 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) u8 status1, status2, mask1, mask2; /* mode_b and PLLA and PLLB reset */ - hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x2C, HDMI_SYSTEM_CTRL); /* How long shall reset be held? */ udelay(10); /* mode_b and PLLA and PLLB reset release */ - hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x20, HDMI_SYSTEM_CTRL); status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1); status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2); @@ -1001,7 +1113,7 @@ static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity) */ if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { /* PS mode d->e. All functions are active */ - hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x80, HDMI_SYSTEM_CTRL); dev_dbg(hdmi->dev, "HDMI running\n"); } @@ -1016,7 +1128,7 @@ static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity) dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi); /* PS mode e->a */ - hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x10, HDMI_SYSTEM_CTRL); } static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = { @@ -1110,10 +1222,58 @@ out: dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi); } +static void sh_hdmi_htop1_init(struct sh_hdmi *hdmi) +{ + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_MODE); + hdmi_htop1_write(hdmi, 0x0000000b, 0x0010); + hdmi_htop1_write(hdmi, 0x00006710, HDMI_HTOP1_HTOP_DCL_FRC_MODE); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_Y1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CB1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CR1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_Y2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CB2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CR2); + hdmi_htop1_write(hdmi, 0x00000008, HDMI_HTOP1_CURRENT); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_TISEMP0_1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_TISEMP2_C); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_PHY_TEST_MODE); + hdmi_htop1_write(hdmi, 0x00000081, HDMI_HTOP1_TISIDRV); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_PLLBW); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISEN); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISDREN); + hdmi_htop1_write(hdmi, 0x00000003, HDMI_HTOP1_ENABLE_SELECTOR); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_MACRO_RESET); + hdmi_htop1_write(hdmi, 0x00000016, HDMI_HTOP1_CISRANGE); + msleep(100); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_ENABLE_SELECTOR); + msleep(100); + hdmi_htop1_write(hdmi, 0x00000003, HDMI_HTOP1_ENABLE_SELECTOR); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_MACRO_RESET); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISEN); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISDREN); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_VIDEO_INPUT); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_CLK_TO_PHY); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_VIDEO_INPUT2); + hdmi_htop1_write(hdmi, 0x0000000a, HDMI_HTOP1_CLK_SET); +} + static int __init sh_hdmi_probe(struct platform_device *pdev) { struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *htop1_res; int irq = platform_get_irq(pdev, 0), ret; struct sh_hdmi *hdmi; long rate; @@ -1121,6 +1281,15 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) if (!res || !pdata || irq < 0) return -ENODEV; + htop1_res = NULL; + if (pdata->flags & HDMI_HAS_HTOP1) { + htop1_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!htop1_res) { + dev_err(&pdev->dev, "htop1 needs register base\n"); + return -EINVAL; + } + } + hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); if (!hdmi) { dev_err(&pdev->dev, "Cannot allocate device data\n"); @@ -1138,6 +1307,15 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto egetclk; } + /* select register access functions */ + if (pdata->flags & HDMI_32BIT_REG) { + hdmi->write = __hdmi_write32; + hdmi->read = __hdmi_read32; + } else { + hdmi->write = __hdmi_write8; + hdmi->read = __hdmi_read8; + } + /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); if (rate > 0) @@ -1176,6 +1354,24 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); + /* init interrupt polarity */ + if (pdata->flags & HDMI_OUTPUT_PUSH_PULL) + hdmi_bit_set(hdmi, 0x02, 0x02, HDMI_SYSTEM_CTRL); + + if (pdata->flags & HDMI_OUTPUT_POLARITY_HI) + hdmi_bit_set(hdmi, 0x01, 0x01, HDMI_SYSTEM_CTRL); + + /* enable htop1 register if needed */ + if (htop1_res) { + hdmi->htop1 = ioremap(htop1_res->start, resource_size(htop1_res)); + if (!hdmi->htop1) { + dev_err(&pdev->dev, "control register region already claimed\n"); + ret = -ENOMEM; + goto emap_htop1; + } + sh_hdmi_htop1_init(hdmi); + } + /* Product and revision IDs are 0 in sh-mobile version */ dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); @@ -1199,6 +1395,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) ecodec: free_irq(irq, hdmi); ereqirq: + if (hdmi->htop1) + iounmap(hdmi->htop1); +emap_htop1: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); iounmap(hdmi->base); @@ -1230,6 +1429,8 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_disable(hdmi->hdmi_clk); clk_put(hdmi->hdmi_clk); + if (hdmi->htop1) + iounmap(hdmi->htop1); iounmap(hdmi->base); release_mem_region(res->start, resource_size(res)); kfree(hdmi); diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h index aff73842d877..85d6738b6c64 100644 --- a/drivers/video/sis/init.h +++ b/drivers/video/sis/init.h @@ -105,51 +105,6 @@ static const unsigned short ModeIndex_1920x1440[] = {0x68, 0x69, 0x00, 0x6b}; static const unsigned short ModeIndex_300_2048x1536[]= {0x6c, 0x6d, 0x00, 0x00}; static const unsigned short ModeIndex_310_2048x1536[]= {0x6c, 0x6d, 0x00, 0x6e}; -static const unsigned short SiS_DRAMType[17][5]={ - {0x0C,0x0A,0x02,0x40,0x39}, - {0x0D,0x0A,0x01,0x40,0x48}, - {0x0C,0x09,0x02,0x20,0x35}, - {0x0D,0x09,0x01,0x20,0x44}, - {0x0C,0x08,0x02,0x10,0x31}, - {0x0D,0x08,0x01,0x10,0x40}, - {0x0C,0x0A,0x01,0x20,0x34}, - {0x0C,0x09,0x01,0x08,0x32}, - {0x0B,0x08,0x02,0x08,0x21}, - {0x0C,0x08,0x01,0x08,0x30}, - {0x0A,0x08,0x02,0x04,0x11}, - {0x0B,0x0A,0x01,0x10,0x28}, - {0x09,0x08,0x02,0x02,0x01}, - {0x0B,0x09,0x01,0x08,0x24}, - {0x0B,0x08,0x01,0x04,0x20}, - {0x0A,0x08,0x01,0x02,0x10}, - {0x09,0x08,0x01,0x01,0x00} -}; - -static const unsigned short SiS_SDRDRAM_TYPE[13][5] = -{ - { 2,12, 9,64,0x35}, - { 1,13, 9,64,0x44}, - { 2,12, 8,32,0x31}, - { 2,11, 9,32,0x25}, - { 1,12, 9,32,0x34}, - { 1,13, 8,32,0x40}, - { 2,11, 8,16,0x21}, - { 1,12, 8,16,0x30}, - { 1,11, 9,16,0x24}, - { 1,11, 8, 8,0x20}, - { 2, 9, 8, 4,0x01}, - { 1,10, 8, 4,0x10}, - { 1, 9, 8, 2,0x00} -}; - -static const unsigned short SiS_DDRDRAM_TYPE[4][5] = -{ - { 2,12, 9,64,0x35}, - { 2,12, 8,32,0x31}, - { 2,11, 8,16,0x21}, - { 2, 9, 8, 4,0x01} -}; - static const unsigned char SiS_MDA_DAC[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 078ca2167d6f..a7a48db64ce2 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -4222,6 +4222,26 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) return 1; /* 32bit */ } +static const unsigned short __devinitconst SiS_DRAMType[17][5] = { + {0x0C,0x0A,0x02,0x40,0x39}, + {0x0D,0x0A,0x01,0x40,0x48}, + {0x0C,0x09,0x02,0x20,0x35}, + {0x0D,0x09,0x01,0x20,0x44}, + {0x0C,0x08,0x02,0x10,0x31}, + {0x0D,0x08,0x01,0x10,0x40}, + {0x0C,0x0A,0x01,0x20,0x34}, + {0x0C,0x09,0x01,0x08,0x32}, + {0x0B,0x08,0x02,0x08,0x21}, + {0x0C,0x08,0x01,0x08,0x30}, + {0x0A,0x08,0x02,0x04,0x11}, + {0x0B,0x0A,0x01,0x10,0x28}, + {0x09,0x08,0x02,0x02,0x01}, + {0x0B,0x09,0x01,0x08,0x24}, + {0x0B,0x08,0x01,0x04,0x20}, + {0x0A,0x08,0x01,0x02,0x10}, + {0x09,0x08,0x01,0x01,0x00} +}; + static int __devinit sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth, int PseudoRankCapacity, int PseudoAdrPinCount, @@ -4231,27 +4251,8 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth unsigned short sr14; unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid; unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage; - static const unsigned short SiS_DRAMType[17][5] = { - {0x0C,0x0A,0x02,0x40,0x39}, - {0x0D,0x0A,0x01,0x40,0x48}, - {0x0C,0x09,0x02,0x20,0x35}, - {0x0D,0x09,0x01,0x20,0x44}, - {0x0C,0x08,0x02,0x10,0x31}, - {0x0D,0x08,0x01,0x10,0x40}, - {0x0C,0x0A,0x01,0x20,0x34}, - {0x0C,0x09,0x01,0x08,0x32}, - {0x0B,0x08,0x02,0x08,0x21}, - {0x0C,0x08,0x01,0x08,0x30}, - {0x0A,0x08,0x02,0x04,0x11}, - {0x0B,0x0A,0x01,0x10,0x28}, - {0x09,0x08,0x02,0x02,0x01}, - {0x0B,0x09,0x01,0x08,0x24}, - {0x0B,0x08,0x01,0x04,0x20}, - {0x0A,0x08,0x01,0x02,0x10}, - {0x09,0x08,0x01,0x01,0x00} - }; - for(k = 0; k <= 16; k++) { + for(k = 0; k < ARRAY_SIZE(SiS_DRAMType); k++) { RankCapacity = buswidth * SiS_DRAMType[k][3]; diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c index 30f7a815a62b..5b6abc6de84b 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -1036,6 +1036,6 @@ static void __exit xxxfb_exit(void) */ module_init(xxxfb_init); -module_exit(xxxfb_remove); +module_exit(xxxfb_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c index ccbfef5e828f..af3ef27ad36c 100644 --- a/drivers/video/smscufx.c +++ b/drivers/video/smscufx.c @@ -846,7 +846,7 @@ static void ufx_raw_rect(struct ufx_data *dev, u16 *cmd, int x, int y, } } -int ufx_handle_damage(struct ufx_data *dev, int x, int y, +static int ufx_handle_damage(struct ufx_data *dev, int x, int y, int width, int height) { size_t packed_line_len = ALIGN((width * 2), 4); @@ -1083,7 +1083,7 @@ static int ufx_ops_open(struct fb_info *info, int user) struct fb_deferred_io *fbdefio; - fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); if (fbdefio) { fbdefio->delay = UFX_DEFIO_WRITE_DELAY; diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 7af1e8166182..8af64148294b 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -893,7 +893,7 @@ static int dlfb_ops_open(struct fb_info *info, int user) struct fb_deferred_io *fbdefio; - fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); if (fbdefio) { fbdefio->delay = DL_DEFIO_WRITE_DELAY; diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 0c8837565bc7..c80e770e1800 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -1276,17 +1276,12 @@ static int viafb_dfph_proc_open(struct inode *inode, struct file *file) static ssize_t viafb_dfph_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { - char buf[20]; - u8 reg_val = 0; - unsigned long length; - if (count < 1) - return -EINVAL; - length = count > 20 ? 20 : count; - if (copy_from_user(&buf[0], buffer, length)) - return -EFAULT; - buf[length - 1] = '\0'; /*Ensure end string */ - if (kstrtou8(buf, 0, ®_val) < 0) - return -EINVAL; + int err; + u8 reg_val; + err = kstrtou8_from_user(buffer, count, 0, ®_val); + if (err) + return err; + viafb_write_reg_mask(CR97, VIACR, reg_val, 0x0f); return count; } @@ -1316,17 +1311,12 @@ static int viafb_dfpl_proc_open(struct inode *inode, struct file *file) static ssize_t viafb_dfpl_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { - char buf[20]; - u8 reg_val = 0; - unsigned long length; - if (count < 1) - return -EINVAL; - length = count > 20 ? 20 : count; - if (copy_from_user(&buf[0], buffer, length)) - return -EFAULT; - buf[length - 1] = '\0'; /*Ensure end string */ - if (kstrtou8(buf, 0, ®_val) < 0) - return -EINVAL; + int err; + u8 reg_val; + err = kstrtou8_from_user(buffer, count, 0, ®_val); + if (err) + return err; + viafb_write_reg_mask(CR99, VIACR, reg_val, 0x0f); return count; } |