* [PATCH] video: SuperH Mobile LCDC Driver V2
@ 2008-05-30 11:02 Magnus Damm
2008-05-30 16:28 ` Krzysztof Helt
0 siblings, 1 reply; 7+ messages in thread
From: Magnus Damm @ 2008-05-30 11:02 UTC (permalink / raw)
To: linux-fbdev-devel; +Cc: iwamatsu, linux-sh, Magnus Damm, lethal, akpm
This is the SuperH Mobile LCDC frame buffer driver V2, adding
support for the LCDC block found in SuperH Mobile processors. The
hardware supports up to two LCD panels per LCDC block, and both RGB
and SYS interfaces can be used to hook up LCD panels/modules.
The device driver is a regular platform driver, so LCD configuration
and board specific hooks are passed to the driver using platform data.
LCD modules using SYS interface often require special configuration
using the SYS bus, and to solve this cleanly the driver provides SYS
interface operations to the board code.
Tested on sh7723 and sh7722 processors with a SYS16A QVGA panel and
WVGA panels using RGB16 and RGB18 interfaces.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
---
Changes since V1:
- updated license header
- improved polling code to use cpu_relax()
- introduced lcdc_wait_bit()
- added lcdc_chan_is_sublcd() function
- broke out start and stop code
drivers/video/Kconfig | 10
drivers/video/Makefile | 2
drivers/video/sh_mobile_lcdcfb.c | 725 ++++++++++++++++++++++++++++++++++++++
include/asm-sh/sh_mobile_lcdc.h | 66 +++
4 files changed, 803 insertions(+)
--- 0001/drivers/video/Kconfig
+++ work/drivers/video/Kconfig 2008-05-30 12:27:52.000000000 +0900
@@ -1839,6 +1839,16 @@ config FB_W100
If unsure, say N.
+config FB_SH_MOBILE_LCDC
+ tristate "SuperH Mobile LCDC framebuffer support"
+ depends on FB && SUPERH
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ default m
+ ---help---
+ Frame buffer driver for the on-chip SH-Mobile LCD controller.
+
config FB_S3C2410
tristate "S3C2410 LCD framebuffer support"
depends on FB && ARCH_S3C2410
--- 0001/drivers/video/Makefile
+++ work/drivers/video/Makefile 2008-05-30 12:27:52.000000000 +0900
@@ -115,6 +115,8 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt450
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
+obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
+obj-$(CONFIG_FB_SH7343VOU) += sh7343_voufb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
--- /dev/null
+++ work/drivers/video/sh_mobile_lcdcfb.c 2008-05-30 19:17:13.000000000 +0900
@@ -0,0 +1,725 @@
+/*
+ * SuperH Mobile LCDC Framebuffer
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <asm/sh_mobile_lcdc.h>
+
+#define PALETTE_NR 16
+
+struct sh_mobile_lcdc_priv;
+struct sh_mobile_lcdc_chan {
+ struct sh_mobile_lcdc_priv *lcdc;
+ unsigned long *reg_offs;
+ unsigned long ldmt1r_value;
+ unsigned long enabled; /* ME and SE in LDCNT2R */
+ struct sh_mobile_lcdc_chan_cfg cfg;
+ u32 pseudo_palette[PALETTE_NR];
+ struct fb_info info;
+ dma_addr_t dma_handle;
+};
+
+struct sh_mobile_lcdc_priv {
+ void __iomem *base;
+ struct clk *clk;
+ unsigned long lddckr;
+ struct sh_mobile_lcdc_chan ch[2];
+};
+
+/* shared registers */
+#define _LDDCKR 0x410
+#define _LDDCKSTPR 0x414
+#define _LDINTR 0x468
+#define _LDSR 0x46c
+#define _LDCNT1R 0x470
+#define _LDCNT2R 0x474
+#define _LDDDSR 0x47c
+#define _LDDWD0R 0x800
+#define _LDDRDR 0x840
+#define _LDDWAR 0x900
+#define _LDDRAR 0x904
+
+/* per-channel registers */
+enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
+ LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
+
+static unsigned long lcdc_offs_mainlcd[] = {
+ [LDDCKPAT1R] = 0x400,
+ [LDDCKPAT2R] = 0x404,
+ [LDMT1R] = 0x418,
+ [LDMT2R] = 0x41c,
+ [LDMT3R] = 0x420,
+ [LDDFR] = 0x424,
+ [LDSM1R] = 0x428,
+ [LDSA1R] = 0x430,
+ [LDMLSR] = 0x438,
+ [LDHCNR] = 0x448,
+ [LDHSYNR] = 0x44c,
+ [LDVLNR] = 0x450,
+ [LDVSYNR] = 0x454,
+ [LDPMR] = 0x460,
+};
+
+static unsigned long lcdc_offs_sublcd[] = {
+ [LDDCKPAT1R] = 0x408,
+ [LDDCKPAT2R] = 0x40c,
+ [LDMT1R] = 0x600,
+ [LDMT2R] = 0x604,
+ [LDMT3R] = 0x608,
+ [LDDFR] = 0x60c,
+ [LDSM1R] = 0x610,
+ [LDSA1R] = 0x618,
+ [LDMLSR] = 0x620,
+ [LDHCNR] = 0x624,
+ [LDHSYNR] = 0x628,
+ [LDVLNR] = 0x62c,
+ [LDVSYNR] = 0x630,
+ [LDPMR] = 0x63c,
+};
+
+#define START_LCDC 0x00000001
+#define LCDC_RESET 0x00000100
+#define DISPLAY_BEU 0x00000008
+#define LCDC_ENABLE 0x00000001
+
+static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
+ int reg_nr, unsigned long data)
+{
+ iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]);
+}
+
+static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
+ int reg_nr)
+{
+ return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
+}
+
+static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
+ unsigned long reg_offs, unsigned long data)
+{
+ iowrite32(data, priv->base + reg_offs);
+}
+
+static unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv,
+ unsigned long reg_offs)
+{
+ return ioread32(priv->base + reg_offs);
+}
+
+static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv,
+ unsigned long reg_offs,
+ unsigned long mask, unsigned long until)
+{
+ while ((lcdc_read(priv, reg_offs) & mask) != until)
+ cpu_relax();
+}
+
+static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
+{
+ return chan->cfg.chan == LCDC_CHAN_SUBLCD;
+}
+
+static void lcdc_sys_write_index(void *handle, unsigned long data)
+{
+ struct sh_mobile_lcdc_chan *ch = handle;
+
+ lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000);
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+}
+
+static void lcdc_sys_write_data(void *handle, unsigned long data)
+{
+ struct sh_mobile_lcdc_chan *ch = handle;
+
+ lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000);
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+}
+
+static unsigned long lcdc_sys_read_data(void *handle)
+{
+ struct sh_mobile_lcdc_chan *ch = handle;
+
+ lcdc_write(ch->lcdc, _LDDRDR, 0x01000000);
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+ udelay(1);
+
+ return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff;
+}
+
+struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
+ lcdc_sys_write_index,
+ lcdc_sys_write_data,
+ lcdc_sys_read_data,
+};
+
+static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
+ int start)
+{
+ unsigned long tmp = lcdc_read(priv, _LDCNT2R);
+ int k;
+
+ /* start or stop the lcdc */
+ if (start)
+ lcdc_write(priv, _LDCNT2R, tmp | START_LCDC);
+ else
+ lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC);
+
+ /* wait until power is applied/stopped on all channels */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
+ if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)
+ while (1) {
+ tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3;
+ if (start && tmp == 3)
+ break;
+ if (!start && tmp == 0)
+ break;
+ cpu_relax();
+ }
+
+ if (!start)
+ lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */
+}
+
+static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
+{
+ struct sh_mobile_lcdc_chan *ch;
+ struct fb_videomode *lcd_cfg;
+ struct sh_mobile_lcdc_board_cfg *board_cfg;
+ unsigned long tmp;
+ int k, m;
+ int ret = 0;
+
+ /* reset */
+ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
+ lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
+
+ /* enable LCDC channels */
+ tmp = lcdc_read(priv, _LDCNT2R);
+ tmp |= priv->ch[0].enabled;
+ tmp |= priv->ch[1].enabled;
+ lcdc_write(priv, _LDCNT2R, tmp);
+
+ /* read data from external memory, avoid using the BEU for now */
+ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);
+
+ /* stop the lcdc first */
+ sh_mobile_lcdc_start_stop(priv, 0);
+
+ /* configure clocks */
+ tmp = priv->lddckr;
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+
+ if (!priv->ch[k].enabled)
+ continue;
+
+ m = ch->cfg.clock_divider;
+ if (!m)
+ continue;
+
+ if (m == 1)
+ m = 1 << 6;
+ tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
+
+ lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000);
+ lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
+ }
+
+ lcdc_write(priv, _LDDCKR, tmp);
+
+ /* start dotclock again */
+ lcdc_write(priv, _LDDCKSTPR, 0);
+ lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
+
+ /* interrupts are disabled */
+ lcdc_write(priv, _LDINTR, 0);
+
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+ lcd_cfg = &ch->cfg.lcd_cfg;
+
+ if (!ch->enabled)
+ continue;
+
+ tmp = ch->ldmt1r_value;
+ tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
+ tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
+ lcdc_write_chan(ch, LDMT1R, tmp);
+
+ /* setup SYS bus */
+ lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
+ lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
+
+ /* horizontal configuration */
+ tmp = lcd_cfg->xres + lcd_cfg->hsync_len;
+ tmp += lcd_cfg->left_margin;
+ tmp += lcd_cfg->right_margin;
+ tmp /= 8; /* HTCN */
+ tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */
+ lcdc_write_chan(ch, LDHCNR, tmp);
+
+ tmp = lcd_cfg->xres;
+ tmp += lcd_cfg->right_margin;
+ tmp /= 8; /* HSYNP */
+ tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */
+ lcdc_write_chan(ch, LDHSYNR, tmp);
+
+ /* power supply */
+ lcdc_write_chan(ch, LDPMR, 0);
+
+ /* vertical configuration */
+ tmp = lcd_cfg->yres + lcd_cfg->vsync_len;
+ tmp += lcd_cfg->upper_margin;
+ tmp += lcd_cfg->lower_margin; /* VTLN */
+ tmp |= lcd_cfg->yres << 16; /* VDLN */
+ lcdc_write_chan(ch, LDVLNR, tmp);
+
+ tmp = lcd_cfg->yres;
+ tmp += lcd_cfg->lower_margin; /* VSYNP */
+ tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */
+ lcdc_write_chan(ch, LDVSYNR, tmp);
+
+ board_cfg = &ch->cfg.board_cfg;
+ if (board_cfg->setup_sys)
+ ret = board_cfg->setup_sys(board_cfg->board_data, ch,
+ &sh_mobile_lcdc_sys_bus_ops);
+ if (ret)
+ return ret;
+ }
+
+ /* --- display_lcdc_data() --- */
+ lcdc_write(priv, _LDINTR, 0x00000f00);
+
+ /* word and long word swap */
+ lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
+
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+
+ if (!priv->ch[k].enabled)
+ continue;
+
+ /* set bpp format in PKF[4:0] */
+ tmp = lcdc_read_chan(ch, LDDFR);
+ tmp &= ~(0x0001001f);
+ tmp |= (priv->ch[k].info.var.bits_per_pixel == 16) ? 3 : 0;
+ lcdc_write_chan(ch, LDDFR, tmp);
+
+ /* point out our frame buffer */
+ lcdc_write_chan(ch, LDSA1R, ch->info.fix.smem_start);
+
+ /* set line size */
+ lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);
+
+ /* continuous read mode */
+ lcdc_write_chan(ch, LDSM1R, 0);
+ }
+
+ /* display output */
+ lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);
+
+ /* start the lcdc */
+ sh_mobile_lcdc_start_stop(priv, 1);
+
+ /* tell the board code to enable the panel */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+ board_cfg = &ch->cfg.board_cfg;
+ if (board_cfg->display_on)
+ board_cfg->display_on(board_cfg->board_data);
+ }
+
+ return 0;
+}
+
+static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
+{
+ struct sh_mobile_lcdc_chan *ch;
+ struct sh_mobile_lcdc_board_cfg *board_cfg;
+ int k;
+
+ /* tell the board code to disable the panel */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+ board_cfg = &ch->cfg.board_cfg;
+ if (board_cfg->display_off)
+ board_cfg->display_off(board_cfg->board_data);
+ }
+
+ /* stop the lcdc */
+ sh_mobile_lcdc_start_stop(priv, 0);
+}
+
+static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
+{
+ int ifm, miftyp;
+
+ switch (ch->cfg.interface_type) {
+ case RGB8: ifm = 0; miftyp = 0; break;
+ case RGB9: ifm = 0; miftyp = 4; break;
+ case RGB12A: ifm = 0; miftyp = 5; break;
+ case RGB12B: ifm = 0; miftyp = 6; break;
+ case RGB16: ifm = 0; miftyp = 7; break;
+ case RGB18: ifm = 0; miftyp = 10; break;
+ case RGB24: ifm = 0; miftyp = 11; break;
+ case SYS8A: ifm = 1; miftyp = 0; break;
+ case SYS8B: ifm = 1; miftyp = 1; break;
+ case SYS8C: ifm = 1; miftyp = 2; break;
+ case SYS8D: ifm = 1; miftyp = 3; break;
+ case SYS9: ifm = 1; miftyp = 4; break;
+ case SYS12: ifm = 1; miftyp = 5; break;
+ case SYS16A: ifm = 1; miftyp = 7; break;
+ case SYS16B: ifm = 1; miftyp = 8; break;
+ case SYS16C: ifm = 1; miftyp = 9; break;
+ case SYS18: ifm = 1; miftyp = 10; break;
+ case SYS24: ifm = 1; miftyp = 11; break;
+ default: goto bad;
+ }
+
+ /* SUBLCD only supports SYS interface */
+ if (lcdc_chan_is_sublcd(ch)) {
+ if (ifm == 0)
+ goto bad;
+ else
+ ifm = 0;
+ }
+
+ ch->ldmt1r_value = (ifm << 12) | miftyp;
+ return 0;
+ bad:
+ return -EINVAL;
+}
+
+static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
+ struct sh_mobile_lcdc_priv *priv)
+{
+ char *str;
+ int icksel;
+
+ switch (clock_source) {
+ case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break;
+ case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break;
+ case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break;
+ default:
+ return -EINVAL;
+ }
+
+ priv->lddckr = icksel << 16;
+
+ if (str) {
+ priv->clk = clk_get(dev, str);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "cannot get clock %s\n", str);
+ return PTR_ERR(priv->clk);
+ }
+
+ clk_enable(priv->clk);
+ }
+
+ return 0;
+}
+
+static int sh_mobile_lcdc_setcolreg(u_int regno,
+ u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ u32 *palette = info->pseudo_palette;
+
+ if (regno >= PALETTE_NR)
+ return -EINVAL;
+
+ /* only FB_VISUAL_TRUECOLOR supported */
+
+ red >>= 16 - info->var.red.length;
+ green >>= 16 - info->var.green.length;
+ blue >>= 16 - info->var.blue.length;
+ transp >>= 16 - info->var.transp.length;
+
+ palette[regno] = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+
+ return 0;
+}
+
+static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
+ .id = "SH Mobile LCDC",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .accel = FB_ACCEL_NONE,
+};
+
+static struct fb_ops sh_mobile_lcdc_ops = {
+ .fb_setcolreg = sh_mobile_lcdc_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
+{
+ switch (bpp) {
+ case 16: /* PKF[4:0] = 00011 - RGB 565 */
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+
+ case 32: /* PKF[4:0] = 00000 - RGB 888
+ * sh7722 pdf says 00RRGGBB but reality is GGBB00RR
+ * this may be because LDDDSR has word swap enabled..
+ */
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 24;
+ var->green.length = 8;
+ var->blue.offset = 16;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ var->bits_per_pixel = bpp;
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+ return 0;
+}
+
+static int sh_mobile_lcdc_remove(struct platform_device *pdev);
+
+static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
+{
+ struct fb_info *info;
+ struct sh_mobile_lcdc_priv *priv;
+ struct sh_mobile_lcdc_info *pdata;
+ struct sh_mobile_lcdc_chan_cfg *cfg;
+ struct resource *res;
+ int error;
+ void *buf;
+ int i, j;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ error = -EINVAL;
+ goto err0;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot find IO resource\n");
+ error = -ENOENT;
+ goto err0;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "cannot allocate device data\n");
+ error = -ENOMEM;
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ pdata = pdev->dev.platform_data;
+
+ j = 0;
+ for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) {
+ priv->ch[j].lcdc = priv;
+ memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
+
+ error = sh_mobile_lcdc_check_interface(&priv->ch[i]);
+ if (error) {
+ dev_err(&pdev->dev, "unsupported interface type\n");
+ goto err1;
+ }
+
+ switch (pdata->ch[i].chan) {
+ case LCDC_CHAN_MAINLCD:
+ priv->ch[j].enabled = 1 << 1;
+ priv->ch[j].reg_offs = lcdc_offs_mainlcd;
+ j++;
+ break;
+ case LCDC_CHAN_SUBLCD:
+ priv->ch[j].enabled = 1 << 2;
+ priv->ch[j].reg_offs = lcdc_offs_sublcd;
+ j++;
+ break;
+ }
+ }
+
+ if (!j) {
+ dev_err(&pdev->dev, "no channels defined\n");
+ error = -EINVAL;
+ goto err1;
+ }
+
+ error = sh_mobile_lcdc_setup_clocks(&pdev->dev,
+ pdata->clock_source, priv);
+ if (error) {
+ dev_err(&pdev->dev, "unable to setup clocks\n");
+ goto err1;
+ }
+
+ priv->lddckr = pdata->lddckr;
+ priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1);
+
+ for (i = 0; i < j; i++) {
+ info = &priv->ch[i].info;
+ cfg = &priv->ch[i].cfg;
+
+ info->fbops = &sh_mobile_lcdc_ops;
+ info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
+ info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres;
+ info->var.activate = FB_ACTIVATE_NOW;
+ error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp);
+ if (error)
+ break;
+
+ info->fix = sh_mobile_lcdc_fix;
+ info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8);
+ info->fix.smem_len = info->fix.line_length * cfg->lcd_cfg.yres;
+
+ buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
+ &priv->ch[i].dma_handle, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&pdev->dev, "unable to allocate buffer\n");
+ error = -ENOMEM;
+ break;
+ }
+
+ info->pseudo_palette = &priv->ch[i].pseudo_palette;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
+ if (error < 0) {
+ dev_err(&pdev->dev, "unable to allocate cmap\n");
+ dma_free_coherent(&pdev->dev, info->fix.smem_len,
+ buf, priv->ch[i].dma_handle);
+ break;
+ }
+
+ memset(buf, 0, info->fix.smem_len);
+ info->fix.smem_start = priv->ch[i].dma_handle;
+ info->screen_base = buf;
+ info->device = &pdev->dev;
+ }
+
+ if (error)
+ goto err1;
+
+ error = sh_mobile_lcdc_start(priv);
+ if (error) {
+ dev_err(&pdev->dev, "unable to start hardware\n");
+ goto err1;
+ }
+
+ for (i = 0; i < j; i++) {
+ error = register_framebuffer(&priv->ch[i].info);
+ if (error < 0)
+ goto err1;
+ }
+
+ for (i = 0; i < j; i++) {
+ info = &priv->ch[i].info;
+ dev_info(info->dev,
+ "registered %s/%s as %dx%d %dbpp.\n",
+ pdev->name,
+ (priv->ch[i].cfg.chan == LCDC_CHAN_MAINLCD) ?
+ "mainlcd" : "sublcd",
+ (int) priv->ch[i].cfg.lcd_cfg.xres,
+ (int) priv->ch[i].cfg.lcd_cfg.yres,
+ priv->ch[i].cfg.bpp);
+ }
+
+ return 0;
+ err1:
+ sh_mobile_lcdc_remove(pdev);
+ err0:
+ return error;
+}
+
+static int sh_mobile_lcdc_remove(struct platform_device *pdev)
+{
+ struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
+ struct fb_info *info;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
+ if (priv->ch[i].info.dev)
+ unregister_framebuffer(&priv->ch[i].info);
+
+ sh_mobile_lcdc_stop(priv);
+
+ for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
+ info = &priv->ch[i].info;
+
+ if (!info->device)
+ continue;
+
+ dma_free_coherent(&pdev->dev, info->fix.smem_len,
+ info->screen_base, priv->ch[i].dma_handle);
+ fb_dealloc_cmap(&info->cmap);
+ }
+
+ if (priv->clk) {
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ }
+
+ if (priv->base)
+ iounmap(priv->base);
+
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver sh_mobile_lcdc_driver = {
+ .driver = {
+ .name = "sh_mobile_lcdc_fb",
+ .owner = THIS_MODULE,
+ },
+ .probe = sh_mobile_lcdc_probe,
+ .remove = sh_mobile_lcdc_remove,
+};
+
+static int __init sh_mobile_lcdc_init(void)
+{
+ return platform_driver_register(&sh_mobile_lcdc_driver);
+}
+
+static void __exit sh_mobile_lcdc_exit(void)
+{
+ platform_driver_unregister(&sh_mobile_lcdc_driver);
+}
+
+module_init(sh_mobile_lcdc_init);
+module_exit(sh_mobile_lcdc_exit);
+
+MODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver");
+MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+++ work/include/asm-sh/sh_mobile_lcdc.h 2008-05-30 12:27:52.000000000 +0900
@@ -0,0 +1,66 @@
+#ifndef __ASM_SH_MOBILE_LCDC_H__
+#define __ASM_SH_MOBILE_LCDC_H__
+
+#include <linux/fb.h>
+
+enum { RGB8, /* 24bpp, 8:8:8 */
+ RGB9, /* 18bpp, 9:9 */
+ RGB12A, /* 24bpp, 12:12 */
+ RGB12B, /* 12bpp */
+ RGB16, /* 16bpp */
+ RGB18, /* 18bpp */
+ RGB24, /* 24bpp */
+ SYS8A, /* 24bpp, 8:8:8 */
+ SYS8B, /* 18bpp, 8:8:2 */
+ SYS8C, /* 18bpp, 2:8:8 */
+ SYS8D, /* 16bpp, 8:8 */
+ SYS9, /* 18bpp, 9:9 */
+ SYS12, /* 24bpp, 12:12 */
+ SYS16A, /* 16bpp */
+ SYS16B, /* 18bpp, 16:2 */
+ SYS16C, /* 18bpp, 2:16 */
+ SYS18, /* 18bpp */
+ SYS24 };/* 24bpp */
+
+enum { LCDC_CHAN_DISABLED = 0,
+ LCDC_CHAN_MAINLCD,
+ LCDC_CHAN_SUBLCD };
+
+enum { LCDC_CLK_BUS, LCDC_CLK_PERIPHERAL, LCDC_CLK_EXTERNAL };
+
+struct sh_mobile_lcdc_sys_bus_cfg {
+ unsigned long ldmt2r;
+ unsigned long ldmt3r;
+};
+
+struct sh_mobile_lcdc_sys_bus_ops {
+ void (*write_index)(void *handle, unsigned long data);
+ void (*write_data)(void *handle, unsigned long data);
+ unsigned long (*read_data)(void *handle);
+};
+
+struct sh_mobile_lcdc_board_cfg {
+ void *board_data;
+ int (*setup_sys)(void *board_data, void *sys_ops_handle,
+ struct sh_mobile_lcdc_sys_bus_ops *sys_ops);
+ void (*display_on)(void *board_data);
+ void (*display_off)(void *board_data);
+};
+
+struct sh_mobile_lcdc_chan_cfg {
+ int chan;
+ int bpp;
+ int interface_type; /* selects RGBn or SYSn I/F, see above */
+ int clock_divider;
+ struct fb_videomode lcd_cfg;
+ struct sh_mobile_lcdc_board_cfg board_cfg;
+ struct sh_mobile_lcdc_sys_bus_cfg sys_bus_cfg; /* only for SYSn I/F */
+};
+
+struct sh_mobile_lcdc_info {
+ unsigned long lddckr;
+ int clock_source;
+ struct sh_mobile_lcdc_chan_cfg ch[2];
+};
+
+#endif /* __ASM_SH_MOBILE_LCDC_H__ */
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH] video: SuperH Mobile LCDC Driver V2
2008-05-30 11:02 [PATCH] video: SuperH Mobile LCDC Driver V2 Magnus Damm
@ 2008-05-30 16:28 ` Krzysztof Helt
2008-05-30 19:06 ` Andrew Morton
0 siblings, 1 reply; 7+ messages in thread
From: Krzysztof Helt @ 2008-05-30 16:28 UTC (permalink / raw)
To: Magnus Damm; +Cc: linux-fbdev-devel, iwamatsu, linux-sh, mano, lethal, akpm
On Fri, 30 May 2008 20:02:39 +0900
Magnus Damm <magnus.damm@gmail.com> wrote:
> This is the SuperH Mobile LCDC frame buffer driver V2, adding
> support for the LCDC block found in SuperH Mobile processors. The
> hardware supports up to two LCD panels per LCDC block, and both RGB
> and SYS interfaces can be used to hook up LCD panels/modules.
>
> The device driver is a regular platform driver, so LCD configuration
> and board specific hooks are passed to the driver using platform data.
> LCD modules using SYS interface often require special configuration
> using the SYS bus, and to solve this cleanly the driver provides SYS
> interface operations to the board code.
>
> Tested on sh7723 and sh7722 processors with a SYS16A QVGA panel and
> WVGA panels using RGB16 and RGB18 interfaces.
>
> Signed-off-by: Magnus Damm <damm@igel.co.jp>
> ---
Acked-by: Krzysztof Helt<krzysztof.h1@wp.pl>
Does anybody know which tag is better for a reviewed patch:
"Acked-by" or "Reviewed-by" ?
Regards,
Krzysztof
----------------------------------------------------------------------
Tanie rozmowy!
Sprawdz >>> http://link.interia.pl/f1e13
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] video: SuperH Mobile LCDC Driver V2
2008-05-30 16:28 ` Krzysztof Helt
@ 2008-05-30 19:06 ` Andrew Morton
2008-05-30 19:44 ` Krzysztof Helt
2008-06-02 6:09 ` Paul Mundt
0 siblings, 2 replies; 7+ messages in thread
From: Andrew Morton @ 2008-05-30 19:06 UTC (permalink / raw)
To: Krzysztof Helt; +Cc: linux-fbdev-devel, iwamatsu, linux-sh, Magnus Damm, lethal
On Fri, 30 May 2008 18:28:03 +0200 Krzysztof Helt <krzysztof.h1@poczta.fm> wrote:
> On Fri, 30 May 2008 20:02:39 +0900
> Magnus Damm <magnus.damm@gmail.com> wrote:
>
> > This is the SuperH Mobile LCDC frame buffer driver V2, adding
> > support for the LCDC block found in SuperH Mobile processors. The
> > hardware supports up to two LCD panels per LCDC block, and both RGB
> > and SYS interfaces can be used to hook up LCD panels/modules.
> >
> > The device driver is a regular platform driver, so LCD configuration
> > and board specific hooks are passed to the driver using platform data.
> > LCD modules using SYS interface often require special configuration
> > using the SYS bus, and to solve this cleanly the driver provides SYS
> > interface operations to the board code.
> >
> > Tested on sh7723 and sh7722 processors with a SYS16A QVGA panel and
> > WVGA panels using RGB16 and RGB18 interfaces.
> >
> > Signed-off-by: Magnus Damm <damm@igel.co.jp>
> > ---
>
> Acked-by: Krzysztof Helt<krzysztof.h1@wp.pl>
>
> Does anybody know which tag is better for a reviewed patch:
> "Acked-by" or "Reviewed-by" ?
>
I consider Reviewed-by: to be much stronger that Acked-by:
You wouldn't believe some of the crap which has had cheerful Acked-by:s :(
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] video: SuperH Mobile LCDC Driver V2
2008-05-30 19:06 ` Andrew Morton
@ 2008-05-30 19:44 ` Krzysztof Helt
2008-05-31 14:25 ` Fabio Giovagnini
2008-06-02 6:09 ` Paul Mundt
1 sibling, 1 reply; 7+ messages in thread
From: Krzysztof Helt @ 2008-05-30 19:44 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-fbdev-devel, iwamatsu, linux-sh, Magnus Damm, lethal
On Fri, 30 May 2008 12:06:06 -0700
Andrew Morton <akpm@linux-foundation.org> wrote:
> On Fri, 30 May 2008 18:28:03 +0200 Krzysztof Helt <krzysztof.h1@poczta.fm> wrote:
>
> > On Fri, 30 May 2008 20:02:39 +0900
> > Magnus Damm <magnus.damm@gmail.com> wrote:
> >
> > > This is the SuperH Mobile LCDC frame buffer driver V2, adding
> > > support for the LCDC block found in SuperH Mobile processors. The
> > > hardware supports up to two LCD panels per LCDC block, and both RGB
> > > and SYS interfaces can be used to hook up LCD panels/modules.
> > >
> > > The device driver is a regular platform driver, so LCD configuration
> > > and board specific hooks are passed to the driver using platform data.
> > > LCD modules using SYS interface often require special configuration
> > > using the SYS bus, and to solve this cleanly the driver provides SYS
> > > interface operations to the board code.
> > >
> > > Tested on sh7723 and sh7722 processors with a SYS16A QVGA panel and
> > > WVGA panels using RGB16 and RGB18 interfaces.
> > >
> > > Signed-off-by: Magnus Damm <damm@igel.co.jp>
> > > ---
> >
> > Acked-by: Krzysztof Helt<krzysztof.h1@wp.pl>
> >
> > Does anybody know which tag is better for a reviewed patch:
> > "Acked-by" or "Reviewed-by" ?
> >
>
> I consider Reviewed-by: to be much stronger that Acked-by:
>
This driver has been really carefully checked.
Reviewed-by: Krzysztof Helt<krzysztof.h1@wp.pl>
Krzysztof
----------------------------------------------------------------------
Tanie rozmowy!
Sprawdz >>> http://link.interia.pl/f1e13
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] video: SuperH Mobile LCDC Driver V2
2008-05-30 19:44 ` Krzysztof Helt
@ 2008-05-31 14:25 ` Fabio Giovagnini
2008-06-02 1:43 ` Magnus Damm
0 siblings, 1 reply; 7+ messages in thread
From: Fabio Giovagnini @ 2008-05-31 14:25 UTC (permalink / raw)
To: Krzysztof Helt
Cc: Andrew Morton, Magnus Damm, linux-fbdev-devel, iwamatsu, linux-sh,
mano, lethal
Which differences are bteween SuperH Mobile LCDC frame buffer and SH2A 7203
LCDC controller?
Thanks a lot.
Best regards
Alle 21:44, venerdì 30 maggio 2008, Krzysztof Helt ha scritto:
> On Fri, 30 May 2008 12:06:06 -0700
>
> Andrew Morton <akpm@linux-foundation.org> wrote:
> > On Fri, 30 May 2008 18:28:03 +0200 Krzysztof Helt <krzysztof.h1@poczta.fm>
wrote:
> > > On Fri, 30 May 2008 20:02:39 +0900
> > >
> > > Magnus Damm <magnus.damm@gmail.com> wrote:
> > > > This is the SuperH Mobile LCDC frame buffer driver V2, adding
> > > > support for the LCDC block found in SuperH Mobile processors. The
> > > > hardware supports up to two LCD panels per LCDC block, and both RGB
> > > > and SYS interfaces can be used to hook up LCD panels/modules.
> > > >
> > > > The device driver is a regular platform driver, so LCD configuration
> > > > and board specific hooks are passed to the driver using platform
> > > > data. LCD modules using SYS interface often require special
> > > > configuration using the SYS bus, and to solve this cleanly the driver
> > > > provides SYS interface operations to the board code.
> > > >
> > > > Tested on sh7723 and sh7722 processors with a SYS16A QVGA panel and
> > > > WVGA panels using RGB16 and RGB18 interfaces.
> > > >
> > > > Signed-off-by: Magnus Damm <damm@igel.co.jp>
> > > > ---
> > >
> > > Acked-by: Krzysztof Helt<krzysztof.h1@wp.pl>
> > >
> > > Does anybody know which tag is better for a reviewed patch:
> > > "Acked-by" or "Reviewed-by" ?
> >
> > I consider Reviewed-by: to be much stronger that Acked-by:
>
> This driver has been really carefully checked.
>
> Reviewed-by: Krzysztof Helt<krzysztof.h1@wp.pl>
>
> Krzysztof
>
> ----------------------------------------------------------------------
> Tanie rozmowy!
> Sprawdz >>> http://link.interia.pl/f1e13
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sh" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Fabio Giovagnini
Aurion s.r.l.
via degli orti 11,
40050 Funo di Argelato (BO)
P.I e C.F.
00885711200
Tel. +39.335.8350919
Fax +39.051.8659009
www.aurion-tech.com
account telefono VoIP skype (www.skype.com):
aurion.giovagnini
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] video: SuperH Mobile LCDC Driver V2
2008-05-31 14:25 ` Fabio Giovagnini
@ 2008-06-02 1:43 ` Magnus Damm
0 siblings, 0 replies; 7+ messages in thread
From: Magnus Damm @ 2008-06-02 1:43 UTC (permalink / raw)
To: Fabio Giovagnini
Cc: linux-fbdev-devel, iwamatsu, linux-sh, lethal, Andrew Morton
On Sat, May 31, 2008 at 11:25 PM, Fabio Giovagnini
<fabio.giovagnini@aurion-tech.com> wrote:
> Which differences are bteween SuperH Mobile LCDC frame buffer and SH2A 7203
> LCDC controller?
Judging from the data sheets the LCDC block in sh7203, sh7763 and
sh7720 are identical. Except different base address for registers and
palette. And maybe IRQ number and pin configuration. =) Anyway, the
sh7760 LCDC is very similar but seems to be missing a few registers.
I'm sure there are other parts that are using this LCDC block as well.
Having a shared driver for sh7203/sh7763/sh7720 and sh7760 makes
sense.
The SuperH Mobile LCDC block (sh7723/sh7722/sh7343) is very different:
Supported pixel formats, hardware interfaces, number of channels,
register layout and register width.
/ magnus
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] video: SuperH Mobile LCDC Driver V2
2008-05-30 19:06 ` Andrew Morton
2008-05-30 19:44 ` Krzysztof Helt
@ 2008-06-02 6:09 ` Paul Mundt
1 sibling, 0 replies; 7+ messages in thread
From: Paul Mundt @ 2008-06-02 6:09 UTC (permalink / raw)
To: Andrew Morton
Cc: Krzysztof Helt, Magnus Damm, linux-fbdev-devel, iwamatsu,
linux-sh, mano
On Fri, May 30, 2008 at 12:06:06PM -0700, Andrew Morton wrote:
> On Fri, 30 May 2008 18:28:03 +0200 Krzysztof Helt <krzysztof.h1@poczta.fm> wrote:
>
> > On Fri, 30 May 2008 20:02:39 +0900
> > Magnus Damm <magnus.damm@gmail.com> wrote:
> >
> > > This is the SuperH Mobile LCDC frame buffer driver V2, adding
> > > support for the LCDC block found in SuperH Mobile processors. The
> > > hardware supports up to two LCD panels per LCDC block, and both RGB
> > > and SYS interfaces can be used to hook up LCD panels/modules.
> > >
> > > The device driver is a regular platform driver, so LCD configuration
> > > and board specific hooks are passed to the driver using platform data.
> > > LCD modules using SYS interface often require special configuration
> > > using the SYS bus, and to solve this cleanly the driver provides SYS
> > > interface operations to the board code.
> > >
> > > Tested on sh7723 and sh7722 processors with a SYS16A QVGA panel and
> > > WVGA panels using RGB16 and RGB18 interfaces.
> > >
> > > Signed-off-by: Magnus Damm <damm@igel.co.jp>
> > > ---
> >
> > Acked-by: Krzysztof Helt<krzysztof.h1@wp.pl>
> >
> > Does anybody know which tag is better for a reviewed patch:
> > "Acked-by" or "Reviewed-by" ?
> >
>
> I consider Reviewed-by: to be much stronger that Acked-by:
>
> You wouldn't believe some of the crap which has had cheerful Acked-by:s :(
In that case:
Acked-by: Paul Mundt <lethal@linux-sh.org>
:-)
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2008-06-02 6:09 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-30 11:02 [PATCH] video: SuperH Mobile LCDC Driver V2 Magnus Damm
2008-05-30 16:28 ` Krzysztof Helt
2008-05-30 19:06 ` Andrew Morton
2008-05-30 19:44 ` Krzysztof Helt
2008-05-31 14:25 ` Fabio Giovagnini
2008-06-02 1:43 ` Magnus Damm
2008-06-02 6:09 ` Paul Mundt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).