summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dma/Kconfig2
-rw-r--r--drivers/dma/shdma.c8
-rw-r--r--drivers/i2c/busses/Kconfig2
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c121
-rw-r--r--drivers/serial/sh-sci.c42
-rw-r--r--drivers/serial/sh-sci.h29
-rw-r--r--drivers/sh/Makefile5
-rw-r--r--drivers/video/Kconfig8
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/sh_mipi_dsi.c505
10 files changed, 664 insertions, 59 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 9e01e96fee94..8344375dc015 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -128,7 +128,7 @@ config TXX9_DMAC
config SH_DMAE
tristate "Renesas SuperH DMAC support"
- depends on SUPERH && SH_DMA
+ depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
depends on !SH_DMA_API
select DMA_ENGINE
help
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index a2a519fd2a24..fb64cf36ba61 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -816,7 +816,7 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data)
return ret;
}
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
static irqreturn_t sh_dmae_err(int irq, void *data)
{
struct sh_dmae_device *shdev = (struct sh_dmae_device *)data;
@@ -1057,7 +1057,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
/* Default transfer size of 32 bytes requires 32-byte alignment */
shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE;
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (!chanirq_res)
@@ -1082,7 +1082,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
#else
chanirq_res = errirq_res;
-#endif /* CONFIG_CPU_SH4 */
+#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */
if (chanirq_res->start == chanirq_res->end &&
!platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
@@ -1129,7 +1129,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
chan_probe_err:
sh_dmae_chan_remove(shdev);
eirqres:
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
free_irq(errirq, shdev);
eirq_err:
#endif
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bceafbfa7268..29e01f6238a7 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -549,7 +549,7 @@ config I2C_SH7760
config I2C_SH_MOBILE
tristate "SuperH Mobile I2C Controller"
- depends on SUPERH
+ depends on SUPERH || ARCH_SHMOBILE
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Renesas SH-Mobile processor.
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index ffb405d7c6f2..598c49acaeb5 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -119,8 +119,10 @@ struct sh_mobile_i2c_data {
struct i2c_adapter adap;
struct clk *clk;
+ u_int8_t icic;
u_int8_t iccl;
u_int8_t icch;
+ u_int8_t flags;
spinlock_t lock;
wait_queue_head_t wait;
@@ -129,15 +131,17 @@ struct sh_mobile_i2c_data {
int sr;
};
+#define IIC_FLAG_HAS_ICIC67 (1 << 0)
+
#define NORMAL_SPEED 100000 /* FAST_SPEED 400000 */
/* Register offsets */
-#define ICDR(pd) (pd->reg + 0x00)
-#define ICCR(pd) (pd->reg + 0x04)
-#define ICSR(pd) (pd->reg + 0x08)
-#define ICIC(pd) (pd->reg + 0x0c)
-#define ICCL(pd) (pd->reg + 0x10)
-#define ICCH(pd) (pd->reg + 0x14)
+#define ICDR 0x00
+#define ICCR 0x04
+#define ICSR 0x08
+#define ICIC 0x0c
+#define ICCL 0x10
+#define ICCH 0x14
/* Register bits */
#define ICCR_ICE 0x80
@@ -155,11 +159,32 @@ struct sh_mobile_i2c_data {
#define ICSR_WAIT 0x02
#define ICSR_DTE 0x01
+#define ICIC_ICCLB8 0x80
+#define ICIC_ICCHB8 0x40
#define ICIC_ALE 0x08
#define ICIC_TACKE 0x04
#define ICIC_WAITE 0x02
#define ICIC_DTEE 0x01
+static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data)
+{
+ if (offs == ICIC)
+ data |= pd->icic;
+
+ iowrite8(data, pd->reg + offs);
+}
+
+static unsigned char iic_rd(struct sh_mobile_i2c_data *pd, int offs)
+{
+ return ioread8(pd->reg + offs);
+}
+
+static void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs,
+ unsigned char set, unsigned char clr)
+{
+ iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr);
+}
+
static void activate_ch(struct sh_mobile_i2c_data *pd)
{
unsigned long i2c_clk;
@@ -187,6 +212,14 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
else
pd->iccl = (u_int8_t)(num/denom);
+ /* one more bit of ICCL in ICIC */
+ if (pd->flags & IIC_FLAG_HAS_ICIC67) {
+ if ((num/denom) > 0xff)
+ pd->icic |= ICIC_ICCLB8;
+ else
+ pd->icic &= ~ICIC_ICCLB8;
+ }
+
/* Calculate the value for icch. From the data sheet:
icch = (p clock / transfer rate) * (H / (L + H)) */
num = i2c_clk * 4;
@@ -196,25 +229,33 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
else
pd->icch = (u_int8_t)(num/denom);
+ /* one more bit of ICCH in ICIC */
+ if (pd->flags & IIC_FLAG_HAS_ICIC67) {
+ if ((num/denom) > 0xff)
+ pd->icic |= ICIC_ICCHB8;
+ else
+ pd->icic &= ~ICIC_ICCHB8;
+ }
+
/* Enable channel and configure rx ack */
- iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Mask all interrupts */
- iowrite8(0, ICIC(pd));
+ iic_wr(pd, ICIC, 0);
/* Set the clock */
- iowrite8(pd->iccl, ICCL(pd));
- iowrite8(pd->icch, ICCH(pd));
+ iic_wr(pd, ICCL, pd->iccl);
+ iic_wr(pd, ICCH, pd->icch);
}
static void deactivate_ch(struct sh_mobile_i2c_data *pd)
{
/* Clear/disable interrupts */
- iowrite8(0, ICSR(pd));
- iowrite8(0, ICIC(pd));
+ iic_wr(pd, ICSR, 0);
+ iic_wr(pd, ICIC, 0);
/* Disable channel */
- iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, 0, ICCR_ICE);
/* Disable clock and mark device as idle */
clk_disable(pd->clk);
@@ -233,35 +274,35 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
switch (op) {
case OP_START: /* issue start and trigger DTE interrupt */
- iowrite8(0x94, ICCR(pd));
+ iic_wr(pd, ICCR, 0x94);
break;
case OP_TX_FIRST: /* disable DTE interrupt and write data */
- iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE, ICIC(pd));
- iowrite8(data, ICDR(pd));
+ iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ iic_wr(pd, ICDR, data);
break;
case OP_TX: /* write data */
- iowrite8(data, ICDR(pd));
+ iic_wr(pd, ICDR, data);
break;
case OP_TX_STOP: /* write data and issue a stop afterwards */
- iowrite8(data, ICDR(pd));
- iowrite8(0x90, ICCR(pd));
+ iic_wr(pd, ICDR, data);
+ iic_wr(pd, ICCR, 0x90);
break;
case OP_TX_TO_RX: /* select read mode */
- iowrite8(0x81, ICCR(pd));
+ iic_wr(pd, ICCR, 0x81);
break;
case OP_RX: /* just read data */
- ret = ioread8(ICDR(pd));
+ ret = iic_rd(pd, ICDR);
break;
case OP_RX_STOP: /* enable DTE interrupt, issue stop */
- iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
- ICIC(pd));
- iowrite8(0xc0, ICCR(pd));
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ iic_wr(pd, ICCR, 0xc0);
break;
case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
- iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
- ICIC(pd));
- ret = ioread8(ICDR(pd));
- iowrite8(0xc0, ICCR(pd));
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ ret = iic_rd(pd, ICDR);
+ iic_wr(pd, ICCR, 0xc0);
break;
}
@@ -367,7 +408,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
unsigned char sr;
int wakeup;
- sr = ioread8(ICSR(pd));
+ sr = iic_rd(pd, ICSR);
pd->sr |= sr; /* remember state */
dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr,
@@ -376,7 +417,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
if (sr & (ICSR_AL | ICSR_TACK)) {
/* don't interrupt transaction - continue to issue stop */
- iowrite8(sr & ~(ICSR_AL | ICSR_TACK), ICSR(pd));
+ iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK));
wakeup = 0;
} else if (pd->msg->flags & I2C_M_RD)
wakeup = sh_mobile_i2c_isr_rx(pd);
@@ -384,7 +425,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
wakeup = sh_mobile_i2c_isr_tx(pd);
if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */
- iowrite8(sr & ~ICSR_WAIT, ICSR(pd));
+ iic_wr(pd, ICSR, sr & ~ICSR_WAIT);
if (wakeup) {
pd->sr |= SW_DONE;
@@ -402,21 +443,21 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg)
}
/* Initialize channel registers */
- iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, 0, ICCR_ICE);
/* Enable channel and configure rx ack */
- iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Set the clock */
- iowrite8(pd->iccl, ICCL(pd));
- iowrite8(pd->icch, ICCH(pd));
+ iic_wr(pd, ICCL, pd->iccl);
+ iic_wr(pd, ICCH, pd->icch);
pd->msg = usr_msg;
pd->pos = -1;
pd->sr = 0;
/* Enable all interrupts to begin with */
- iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE | ICIC_DTEE, ICIC(pd));
+ iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
return 0;
}
@@ -451,7 +492,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
retry_count = 1000;
again:
- val = ioread8(ICSR(pd));
+ val = iic_rd(pd, ICSR);
dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr);
@@ -576,6 +617,12 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
goto err_irq;
}
+ /* The IIC blocks on SH-Mobile ARM processors
+ * come with two new bits in ICIC.
+ */
+ if (size > 0x17)
+ pd->flags |= IIC_FLAG_HAS_ICIC67;
+
/* Enable Runtime PM for this device.
*
* Also tell the Runtime PM core to ignore children
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 5f90fcd7d107..c291b3add1d2 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -346,6 +346,27 @@ static int scif_rxfill(struct uart_port *port)
return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;
}
}
+#elif defined(CONFIG_ARCH_SH7372)
+static int scif_txfill(struct uart_port *port)
+{
+ if (port->type == PORT_SCIFA)
+ return sci_in(port, SCFDR) >> 8;
+ else
+ return sci_in(port, SCTFDR);
+}
+
+static int scif_txroom(struct uart_port *port)
+{
+ return port->fifosize - scif_txfill(port);
+}
+
+static int scif_rxfill(struct uart_port *port)
+{
+ if (port->type == PORT_SCIFA)
+ return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
+ else
+ return sci_in(port, SCRFDR);
+}
#else
static int scif_txfill(struct uart_port *port)
{
@@ -683,7 +704,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
u16 ssr = sci_in(port, SCxSR);
/* Disable future Rx interrupts */
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
disable_irq_nosync(irq);
scr |= 0x4000;
} else {
@@ -928,7 +949,7 @@ static void sci_dma_tx_complete(void *arg)
if (!uart_circ_empty(xmit)) {
schedule_work(&s->work_tx);
- } else if (port->type == PORT_SCIFA) {
+ } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE);
}
@@ -1184,7 +1205,7 @@ static void sci_start_tx(struct uart_port *port)
unsigned short ctrl;
#ifdef CONFIG_SERIAL_SH_SCI_DMA
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 new, scr = sci_in(port, SCSCR);
if (s->chan_tx)
new = scr | 0x8000;
@@ -1197,7 +1218,7 @@ static void sci_start_tx(struct uart_port *port)
s->cookie_tx < 0)
schedule_work(&s->work_tx);
#endif
- if (!s->chan_tx || port->type == PORT_SCIFA) {
+ if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE);
@@ -1210,7 +1231,7 @@ static void sci_stop_tx(struct uart_port *port)
/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x8000;
ctrl &= ~SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl);
@@ -1222,7 +1243,7 @@ static void sci_start_rx(struct uart_port *port)
/* Set RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl |= sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x4000;
sci_out(port, SCSCR, ctrl);
}
@@ -1233,7 +1254,7 @@ static void sci_stop_rx(struct uart_port *port)
/* Clear RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x4000;
ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE);
sci_out(port, SCSCR, ctrl);
@@ -1271,7 +1292,7 @@ static void rx_timer_fn(unsigned long arg)
struct uart_port *port = &s->port;
u16 scr = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
scr &= ~0x4000;
enable_irq(s->irqs[1]);
}
@@ -1524,6 +1545,8 @@ static const char *sci_type(struct uart_port *port)
return "scif";
case PORT_SCIFA:
return "scifa";
+ case PORT_SCIFB:
+ return "scifb";
}
return NULL;
@@ -1612,6 +1635,9 @@ static int __devinit sci_init_single(struct platform_device *dev,
port->line = index;
switch (p->type) {
+ case PORT_SCIFB:
+ port->fifosize = 256;
+ break;
case PORT_SCIFA:
port->fifosize = 64;
break;
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index f70c49f915fa..9b52f77a9305 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -322,7 +322,7 @@
#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\
static inline unsigned int sci_##name##_in(struct uart_port *port) \
{ \
- if (port->type == PORT_SCIF) { \
+ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
SCI_IN(scif_size, scif_offset) \
} else { /* PORT_SCI or PORT_SCIFA */ \
SCI_IN(sci_size, sci_offset); \
@@ -330,7 +330,7 @@
} \
static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
{ \
- if (port->type == PORT_SCIF) { \
+ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
SCI_OUT(scif_size, scif_offset, value) \
} else { /* PORT_SCI or PORT_SCIFA */ \
SCI_OUT(sci_size, sci_offset, value); \
@@ -384,8 +384,12 @@
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
+ defined(CONFIG_ARCH_SH7377)
+#define SCIF_FNS(name, scif_offset, scif_size) \
+ CPU_SCIF_FNS(name, scif_offset, scif_size)
+#elif defined(CONFIG_ARCH_SH7372)
+#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \
+ CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size)
#define SCIF_FNS(name, scif_offset, scif_size) \
CPU_SCIF_FNS(name, scif_offset, scif_size)
#else
@@ -422,8 +426,7 @@
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
+ defined(CONFIG_ARCH_SH7377)
SCIF_FNS(SCSMR, 0x00, 16)
SCIF_FNS(SCBRR, 0x04, 8)
@@ -436,6 +439,20 @@ SCIF_FNS(SCFDR, 0x1c, 16)
SCIF_FNS(SCxTDR, 0x20, 8)
SCIF_FNS(SCxRDR, 0x24, 8)
SCIF_FNS(SCLSR, 0x00, 0)
+#elif defined(CONFIG_ARCH_SH7372)
+SCIF_FNS(SCSMR, 0x00, 16)
+SCIF_FNS(SCBRR, 0x04, 8)
+SCIF_FNS(SCSCR, 0x08, 16)
+SCIF_FNS(SCTDSR, 0x0c, 16)
+SCIF_FNS(SCFER, 0x10, 16)
+SCIF_FNS(SCxSR, 0x14, 16)
+SCIF_FNS(SCFCR, 0x18, 16)
+SCIF_FNS(SCFDR, 0x1c, 16)
+SCIF_FNS(SCTFDR, 0x38, 16)
+SCIF_FNS(SCRFDR, 0x3c, 16)
+SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8)
+SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8)
+SCIF_FNS(SCLSR, 0x00, 0)
#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
defined(CONFIG_CPU_SUBTYPE_SH7724)
SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16)
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index 78bb5127abd0..08fc653a825c 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -1,9 +1,10 @@
#
# Makefile for the SuperH specific drivers.
#
+obj-y := clk.o intc.o
+
obj-$(CONFIG_SUPERHYWAY) += superhyway/
obj-$(CONFIG_MAPLE) += maple/
+
obj-$(CONFIG_GENERIC_GPIO) += pfc.o
-obj-$(CONFIG_SUPERH) += clk.o
obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o
-obj-y += intc.o
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3d94a1471724..a9f9e5eaa040 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1895,6 +1895,13 @@ config FB_W100
If unsure, say N.
+config SH_MIPI_DSI
+ tristate
+ depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
+
+config SH_LCD_MIPI_DSI
+ bool
+
config FB_SH_MOBILE_LCDC
tristate "SuperH Mobile LCDC framebuffer support"
depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
@@ -1903,6 +1910,7 @@ config FB_SH_MOBILE_LCDC
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
+ select SH_MIPI_DSI if SH_LCD_MIPI_DSI
---help---
Frame buffer driver for the on-chip SH-Mobile LCD controller.
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ddc2af2ba45b..3c3bf867ef18 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -123,6 +123,7 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
+obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o
obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-y += omap2/
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
new file mode 100644
index 000000000000..017ae9f47d36
--- /dev/null
+++ b/drivers/video/sh_mipi_dsi.c
@@ -0,0 +1,505 @@
+/*
+ * Renesas SH-mobile MIPI DSI support
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <video/mipi_display.h>
+#include <video/sh_mipi_dsi.h>
+#include <video/sh_mobile_lcdc.h>
+
+#define CMTSRTCTR 0x80d0
+#define CMTSRTREQ 0x8070
+
+#define DSIINTE 0x0060
+
+/* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */
+#define MAX_SH_MIPI_DSI 2
+
+struct sh_mipi {
+ void __iomem *base;
+ struct clk *dsit_clk;
+ struct clk *dsip_clk;
+};
+
+static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI];
+
+/* Protect the above array */
+static DEFINE_MUTEX(array_lock);
+
+static struct sh_mipi *sh_mipi_by_handle(int handle)
+{
+ if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0)
+ return NULL;
+
+ return mipi_dsi[handle];
+}
+
+static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd,
+ u8 cmd, u8 param)
+{
+ u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8);
+ int cnt = 100;
+
+ /* transmit a short packet to LCD panel */
+ iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */
+ iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */
+
+ while ((ioread32(mipi->base + 0x8070) & 1) && --cnt)
+ udelay(1);
+
+ return cnt ? 0 : -ETIMEDOUT;
+}
+
+#define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \
+ -EINVAL : (c) - 1)
+
+static int sh_mipi_dcs(int handle, u8 cmd)
+{
+ struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
+ if (!mipi)
+ return -ENODEV;
+ return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
+}
+
+static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param)
+{
+ struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
+ if (!mipi)
+ return -ENODEV;
+ return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd,
+ param);
+}
+
+static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable)
+{
+ /*
+ * enable LCDC data tx, transition to LPS after completion of each HS
+ * packet
+ */
+ iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */
+}
+
+static void sh_mipi_shutdown(struct platform_device *pdev)
+{
+ struct sh_mipi *mipi = platform_get_drvdata(pdev);
+
+ sh_mipi_dsi_enable(mipi, false);
+}
+
+static void mipi_display_on(void *arg)
+{
+ struct sh_mipi *mipi = arg;
+
+ sh_mipi_dsi_enable(mipi, true);
+}
+
+static void mipi_display_off(void *arg)
+{
+ struct sh_mipi *mipi = arg;
+
+ sh_mipi_dsi_enable(mipi, false);
+}
+
+static int __init sh_mipi_setup(struct sh_mipi *mipi,
+ struct sh_mipi_dsi_info *pdata)
+{
+ void __iomem *base = mipi->base;
+ struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
+ u32 pctype, datatype, pixfmt;
+ u32 linelength;
+ bool yuv;
+
+ /* Select data format */
+ switch (pdata->data_format) {
+ case MIPI_RGB888:
+ pctype = 0;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_RGB565:
+ pctype = 1;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = false;
+ break;
+ case MIPI_RGB666_LP:
+ pctype = 2;
+ datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_RGB666:
+ pctype = 3;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
+ linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
+ yuv = false;
+ break;
+ case MIPI_BGR888:
+ pctype = 8;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_BGR565:
+ pctype = 9;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = false;
+ break;
+ case MIPI_BGR666_LP:
+ pctype = 0xa;
+ datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_BGR666:
+ pctype = 0xb;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
+ linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
+ yuv = false;
+ break;
+ case MIPI_YUYV:
+ pctype = 4;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = true;
+ break;
+ case MIPI_UYVY:
+ pctype = 5;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = true;
+ break;
+ case MIPI_YUV420_L:
+ pctype = 6;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
+ pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
+ linelength = (ch->lcd_cfg.xres * 12 + 7) / 8;
+ yuv = true;
+ break;
+ case MIPI_YUV420:
+ pctype = 7;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
+ pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
+ /* Length of U/V line */
+ linelength = (ch->lcd_cfg.xres + 1) / 2;
+ yuv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((yuv && ch->interface_type != YUV422) ||
+ (!yuv && ch->interface_type != RGB24))
+ return -EINVAL;
+
+ /* reset DSI link */
+ iowrite32(0x00000001, base); /* SYSCTRL */
+ /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */
+ udelay(50);
+ iowrite32(0x00000000, base); /* SYSCTRL */
+
+ /* setup DSI link */
+
+ /*
+ * Default = ULPS enable |
+ * Contention detection enabled |
+ * EoT packet transmission enable |
+ * CRC check enable |
+ * ECC check enable
+ * additionally enable first two lanes
+ */
+ iowrite32(0x00003703, base + 0x04); /* SYSCONF */
+ /*
+ * T_wakeup = 0x7000
+ * T_hs-trail = 3
+ * T_hs-prepare = 3
+ * T_clk-trail = 3
+ * T_clk-prepare = 2
+ */
+ iowrite32(0x70003332, base + 0x08); /* TIMSET */
+ /* no responses requested */
+ iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */
+ /* request response to packets of type 0x28 */
+ iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */
+ /* High-speed transmission timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */
+ /* LP reception timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */
+ /* Turn-around timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */
+ /* Peripheral reset timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */
+ /* Enable timeout counters */
+ iowrite32(0x00000f00, base + 0x30); /* DSICTRL */
+ /* Interrupts not used, disable all */
+ iowrite32(0, base + DSIINTE);
+ /* DSI-Tx bias on */
+ iowrite32(0x00000001, base + 0x70); /* PHYCTRL */
+ udelay(200);
+ /* Deassert resets, power on, set multiplier */
+ iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */
+
+ /* setup l-bridge */
+
+ /*
+ * Enable transmission of all packets,
+ * transmit LPS after each HS packet completion
+ */
+ iowrite32(0x00000006, base + 0x8000); /* DTCTR */
+ /* VSYNC width = 2 (<< 17) */
+ iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */
+ /*
+ * Non-burst mode with sync pulses: VSE and HSE are output,
+ * HSA period allowed, no commands in LP
+ */
+ iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */
+ /*
+ * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see
+ * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default
+ * (unused, since VMCTR2[HSABM] = 0)
+ */
+ iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */
+
+ msleep(5);
+
+ /* setup LCD panel */
+
+ /* cf. drivers/video/omap/lcd_mipid.c */
+ sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(120);
+ /*
+ * [7] - Page Address Mode
+ * [6] - Column Address Mode
+ * [5] - Page / Column Address Mode
+ * [4] - Display Device Line Refresh Order
+ * [3] - RGB/BGR Order
+ * [2] - Display Data Latch Data Order
+ * [1] - Flip Horizontal
+ * [0] - Flip Vertical
+ */
+ sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+ /* cf. set_data_lines() */
+ sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT,
+ pixfmt << 4);
+ sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+static int __init sh_mipi_probe(struct platform_device *pdev)
+{
+ struct sh_mipi *mipi;
+ struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ unsigned long rate, f_current;
+ int idx = pdev->id, ret;
+ char dsip_clk[] = "dsi.p_clk";
+
+ if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
+ return -ENODEV;
+
+ mutex_lock(&array_lock);
+ if (idx < 0)
+ for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++)
+ ;
+
+ if (idx == ARRAY_SIZE(mipi_dsi)) {
+ ret = -EBUSY;
+ goto efindslot;
+ }
+
+ mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
+ if (!mipi) {
+ ret = -ENOMEM;
+ goto ealloc;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "MIPI register region already claimed\n");
+ ret = -EBUSY;
+ goto ereqreg;
+ }
+
+ mipi->base = ioremap(res->start, resource_size(res));
+ if (!mipi->base) {
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk");
+ if (IS_ERR(mipi->dsit_clk)) {
+ ret = PTR_ERR(mipi->dsit_clk);
+ goto eclktget;
+ }
+
+ f_current = clk_get_rate(mipi->dsit_clk);
+ /* 80MHz required by the datasheet */
+ rate = clk_round_rate(mipi->dsit_clk, 80000000);
+ if (rate > 0 && rate != f_current)
+ ret = clk_set_rate(mipi->dsit_clk, rate);
+ else
+ ret = rate;
+ if (ret < 0)
+ goto esettrate;
+
+ dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
+
+ sprintf(dsip_clk, "dsi%1.1dp_clk", idx);
+ mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk);
+ if (IS_ERR(mipi->dsip_clk)) {
+ ret = PTR_ERR(mipi->dsip_clk);
+ goto eclkpget;
+ }
+
+ f_current = clk_get_rate(mipi->dsip_clk);
+ /* Between 10 and 50MHz */
+ rate = clk_round_rate(mipi->dsip_clk, 24000000);
+ if (rate > 0 && rate != f_current)
+ ret = clk_set_rate(mipi->dsip_clk, rate);
+ else
+ ret = rate;
+ if (ret < 0)
+ goto esetprate;
+
+ dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate);
+
+ msleep(10);
+
+ ret = clk_enable(mipi->dsit_clk);
+ if (ret < 0)
+ goto eclkton;
+
+ ret = clk_enable(mipi->dsip_clk);
+ if (ret < 0)
+ goto eclkpon;
+
+ mipi_dsi[idx] = mipi;
+
+ ret = sh_mipi_setup(mipi, pdata);
+ if (ret < 0)
+ goto emipisetup;
+
+ mutex_unlock(&array_lock);
+ platform_set_drvdata(pdev, mipi);
+
+ /* Set up LCDC callbacks */
+ pdata->lcd_chan->board_cfg.board_data = mipi;
+ pdata->lcd_chan->board_cfg.display_on = mipi_display_on;
+ pdata->lcd_chan->board_cfg.display_off = mipi_display_off;
+
+ return 0;
+
+emipisetup:
+ mipi_dsi[idx] = NULL;
+ clk_disable(mipi->dsip_clk);
+eclkpon:
+ clk_disable(mipi->dsit_clk);
+eclkton:
+esetprate:
+ clk_put(mipi->dsip_clk);
+eclkpget:
+esettrate:
+ clk_put(mipi->dsit_clk);
+eclktget:
+ iounmap(mipi->base);
+emap:
+ release_mem_region(res->start, resource_size(res));
+ereqreg:
+ kfree(mipi);
+ealloc:
+efindslot:
+ mutex_unlock(&array_lock);
+
+ return ret;
+}
+
+static int __exit sh_mipi_remove(struct platform_device *pdev)
+{
+ struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct sh_mipi *mipi = platform_get_drvdata(pdev);
+ int i, ret;
+
+ mutex_lock(&array_lock);
+
+ for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++)
+ ;
+
+ if (i == ARRAY_SIZE(mipi_dsi)) {
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ mipi_dsi[i] = NULL;
+ }
+
+ mutex_unlock(&array_lock);
+
+ if (ret < 0)
+ return ret;
+
+ pdata->lcd_chan->board_cfg.display_on = NULL;
+ pdata->lcd_chan->board_cfg.display_off = NULL;
+ pdata->lcd_chan->board_cfg.board_data = NULL;
+
+ clk_disable(mipi->dsip_clk);
+ clk_disable(mipi->dsit_clk);
+ clk_put(mipi->dsit_clk);
+ clk_put(mipi->dsip_clk);
+ iounmap(mipi->base);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ platform_set_drvdata(pdev, NULL);
+ kfree(mipi);
+
+ return 0;
+}
+
+static struct platform_driver sh_mipi_driver = {
+ .remove = __exit_p(sh_mipi_remove),
+ .shutdown = sh_mipi_shutdown,
+ .driver = {
+ .name = "sh-mipi-dsi",
+ },
+};
+
+static int __init sh_mipi_init(void)
+{
+ return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe);
+}
+module_init(sh_mipi_init);
+
+static void __exit sh_mipi_exit(void)
+{
+ platform_driver_unregister(&sh_mipi_driver);
+}
+module_exit(sh_mipi_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver");
+MODULE_LICENSE("GPL v2");