Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [RFC PATCH 0/4] Common Display Framework-TF
From: Marcus Lorentzon @ 2013-02-08 14:54 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa,
	dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org,
	linux-samsung-soc@vger.kernel.org, kyungmin.park@samsung.com,
	m.szyprowski@samsung.com, Daniel Vetter, rob@ti.com, Vikas Sajjan,
	inki.dae@samsung.com, dh09.lee@samsung.com,
	ville.syrjala@intel.com, s.nawrocki@samsung.com
In-Reply-To: <5115055B.2080509@ti.com>

On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>
>> When we do that we stop->setup->start during blanking. So our "DSS" is
>> optimized to be able to do that without getting blocked. All DSI video
>> mode panels (and DPI) products we have done so far have not had any
>> issue with that (as long as DSI HS clock is set to continuous). I think
>> this approach is less platform dependant, as long as there is no SoC
>> that take more than a blanking period to reconfigure.
> So do you stop, setup and start the link with CPU, and this has to be
> happen during blanking? Isn't that prone to errors? Or did you mean that
> the hardware handles that automatically?
>
> In OMAP DSS there are so called shadow registers, that can be programmed
> at any time. The you set a bit (GO bit), which tells the hardware to
> take the new settings into use at the next vblank.
>
>  From DSI driver's perspective the link is never stopped when
> reconfiguring the video timings. However, many other settings have to be
> configured when the link is disabled.

Yeah, you lucky guys with the GO bit ;). No, we actually do CPU 
stop,setup,start. But since it is video mode, master is driving the sync 
so it is not a hard deadline. It is enough to restart before pixels 
start to degrade. On an LCD that is not so much time, but on an OLED it 
could be 10 secs :). Anyway, we have had several mass products with this 
soft solution and it has worked well.
>>>>> In OMAP you can configure the DSI pins quite freely. We have the
>>>>> following struct:
>>>>>
>>>>> struct omap_dsi_pin_config {
>>>>>       int num_pins;
>>>>>       /*
>>>>>        * pin numbers in the following order:
>>>>>        * clk+, clk-
>>>>>        * data1+, data1-
>>>>>        * data2+, data2-
>>>>>        * ...
>>>>>        */
>>>>>       int pins[OMAP_DSS_MAX_DSI_PINS];
>>>>> };
>>>>>
>> I think it still is OMAP specifics and doesn't belong in the panel
>> drivers any longer. If you revisit this requirement in the CDF context
>> where DSI ifc parameters should describe how to interface with a panel
>> outside the SoC, there can't really be any dependencies on SoC internal
>> routing. As you say, this is inside pinmux, so how can that affect the
>> SoC external interface? I would suggest moving this to dispc-dsilink DT
>> settings that are activated on dsilink->enable/disable. At least that is
>> how I plan to solve similar STE SoC specific DSI config settings that
>> are not really CDF panel generic, like some DPhy trim settings. They do
>> depend on the panel and clock speed, but they are more product specific
>> than panel driver specific. Then if there are these type of settings
>> that every SoC have, then we could look at standardize those. But for
>> starters I would try to keep it in product/board-DT per DSI link. So we
>> should try to differentiate between DSI host and slave bus params and
>> keep slave params in panel driver.
> Ok, I think I was being a bit vague here. I explained the OMAP DSI
> routing not because I meant that this API is specific to that, but
> because it explains why this kind of routing info is needed, and a
> bitmask is not enough.
>
> If you look at the omap_dsi_pin_config struct, there's nothing OMAP
> specific there (except the names =). All it tells is that this device
> uses N DSI pins, and the device's clk+ function should be connected to
> pin X on the DSI master, clk- should be connected to pin Y, etc. X and Y
> are integers, and what they mean is specific to the DSI master.
>
> When the DSI master is OMAP's DSI, the OMAP DSI driver does the pin
> configuration as I explained. When the DSI master is something else,
> say, a DSI bridge, it does whatever it needs to do (which could be
> nothing) to assign a particular DSI function to a pin.
>
I understand, but removing the omap prefix doesn't mean it has to go in 
the panel slave port/bus settings. I still can't see why this should be 
configuration on the panel driver and not the DSI master driver. Number 
of pins might be useful since you might start with one lane and then 
activate the rest. But partial muxing (pre pinmux) doesn't seem to be 
something the panel should control or know anything about. Sounds like 
normal platform/DT data per product/board.

/BR
/Marcus


^ permalink raw reply

* [PATCH v2 0/1] OMAP4: DSS: Add panel for Blaze Tablet boards
From: Ruslan Bilovol @ 2013-02-08 15:43 UTC (permalink / raw)
  To: andi, tomi.valkeinen, FlorianSchandinat, linux-fbdev,
	linux-kernel, linux-omap

Hi,

This patch adds support for TC358765 DSI-to-LVDS transmitter
from Toshiba, that is used in OMAP4 Blaze Tablet development
platform. It was originally developed a long time ago and
was used internally survived few kernel migrations.
Different people worked in this driver during last two
years changing its sources to what each new version of
kernel expects.

Current version is ported from internal kernel v3.4 to 3.8.0-rc6
Tested basic functioning under busybox.

------------------------------------------------
v2:
- added returns of correct errno instead of magic numbers
- used delays, based on hrtimers where possible
- fixed typo
- switched to devres management of resources instead of raw
  calls


Tomi Valkeinen (1):
  OMAP4: DSS: Add panel for Blaze Tablet boards

 drivers/video/omap2/displays/Kconfig          |   15 +
 drivers/video/omap2/displays/Makefile         |    1 +
 drivers/video/omap2/displays/panel-tc358765.c | 1000 +++++++++++++++++++++++++
 drivers/video/omap2/displays/panel-tc358765.h |  170 +++++
 include/video/omap-panel-tc358765.h           |   53 ++
 5 files changed, 1239 insertions(+)
 create mode 100644 drivers/video/omap2/displays/panel-tc358765.c
 create mode 100644 drivers/video/omap2/displays/panel-tc358765.h
 create mode 100644 include/video/omap-panel-tc358765.h

-- 
1.7.9.5


^ permalink raw reply

* [PATCH v2 1/1] OMAP4: DSS: Add panel for Blaze Tablet boards
From: Ruslan Bilovol @ 2013-02-08 15:43 UTC (permalink / raw)
  To: andi, tomi.valkeinen, FlorianSchandinat, linux-fbdev,
	linux-kernel, linux-omap
In-Reply-To: <1360338220-12753-1-git-send-email-ruslan.bilovol@ti.com>

From: Tomi Valkeinen <tomi.valkeinen@ti.com>

TC358765 is DSI-to-LVDS transmitter from Toshiba, used in
OMAP44XX Blaze Tablet and Blaze Tablet2 boards.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
Signed-off-by: Sergiy Kibrik <sergiy.kibrik@globallogic.com>
Signed-off-by: Volodymyr Riazantsev <v.riazantsev@ti.com>
Signed-off-by: Ruslan Bilovol <ruslan.bilovol@ti.com>
---
 drivers/video/omap2/displays/Kconfig          |   15 +
 drivers/video/omap2/displays/Makefile         |    1 +
 drivers/video/omap2/displays/panel-tc358765.c | 1000 +++++++++++++++++++++++++
 drivers/video/omap2/displays/panel-tc358765.h |  170 +++++
 include/video/omap-panel-tc358765.h           |   53 ++
 5 files changed, 1239 insertions(+)
 create mode 100644 drivers/video/omap2/displays/panel-tc358765.c
 create mode 100644 drivers/video/omap2/displays/panel-tc358765.h
 create mode 100644 include/video/omap-panel-tc358765.h

diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index c3853c9..c6ab261 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -72,4 +72,19 @@ config PANEL_N8X0
 	depends on BACKLIGHT_CLASS_DEVICE
 	help
 	  This is the LCD panel used on Nokia N8x0
+
+config PANEL_TC358765
+	tristate "Toshiba TC358765 DSI-2-LVDS bridge"
+	depends on OMAP2_DSS_DSI && I2C
+	select BACKLIGHT_CLASS_DEVICE
+	help
+		Toshiba TC358765 DSI-2-LVDS chip with 1024x768 panel,
+		which can be found in OMAP4-based Blaze Tablet boards
+		and some other boards.
+
+config TC358765_DEBUG
+	bool "Toshiba TC358765 DSI-2-LVDS chip debug"
+	depends on PANEL_TC358765 && DEBUG_FS
+	help
+	  Support of TC358765 DSI-2-LVDS chip register access via debugfs
 endmenu
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
index 58a5176..b9f2ab6 100644
--- a/drivers/video/omap2/displays/Makefile
+++ b/drivers/video/omap2/displays/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_PANEL_PICODLP) +=  panel-picodlp.o
 obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
 obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o
 obj-$(CONFIG_PANEL_N8X0) += panel-n8x0.o
+obj-$(CONFIG_PANEL_TC358765) += panel-tc358765.o
diff --git a/drivers/video/omap2/displays/panel-tc358765.c b/drivers/video/omap2/displays/panel-tc358765.c
new file mode 100644
index 0000000..7e4f9f4
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-tc358765.c
@@ -0,0 +1,1000 @@
+/*
+ * Toshiba TC358765 DSI-to-LVDS chip driver
+ *
+ * Copyright (C) Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> (3.0)
+ * Author: Sergii Kibrik <sergiikibrik@ti.com> (3.4)
+ * Author: Ruslan Bilovol <ruslan.bilovol@ti.com> (3.8+)
+ *
+ * Based on original version from Jerry Alexander <x0135174@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-tc358765.h>
+
+#include "panel-tc358765.h"
+
+#define A_RO 0x1
+#define A_WO 0x2
+#define A_RW (A_RO|A_WO)
+
+#define FLD_MASK(start, end)	(((1 << ((start) - (end) + 1)) - 1) << (end))
+#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
+#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
+#define FLD_MOD(orig, val, start, end) \
+	(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
+
+static struct omap_video_timings tc358765_timings;
+static struct tc358765_board_data *get_board_data(struct omap_dss_device
+					*dssdev) __attribute__ ((unused));
+
+/* device private data structure */
+struct tc358765_data {
+	struct mutex lock;
+
+	struct omap_dss_device *dssdev;
+
+	int channel0;
+	int channel1;
+
+	struct omap_dsi_pin_config pin_config;
+};
+
+static struct {
+	struct i2c_client *client;
+	struct mutex xfer_lock;
+} *tc358765_i2c;
+
+
+#ifdef CONFIG_TC358765_DEBUG
+
+struct {
+	struct device *dev;
+	struct dentry *dir;
+} tc358765_debug;
+
+struct tc358765_reg {
+	const char *name;
+	u16 reg;
+	u8 perm:2;
+} tc358765_regs[] = {
+	/* DSI D-PHY Layer Registers */
+	{ "D0W_DPHYCONTTX", D0W_DPHYCONTTX, A_RW },
+	{ "CLW_DPHYCONTRX", CLW_DPHYCONTRX, A_RW },
+	{ "D0W_DPHYCONTRX", D0W_DPHYCONTRX, A_RW },
+	{ "D1W_DPHYCONTRX", D1W_DPHYCONTRX, A_RW },
+	{ "D2W_DPHYCONTRX", D2W_DPHYCONTRX, A_RW },
+	{ "D3W_DPHYCONTRX", D3W_DPHYCONTRX, A_RW },
+	{ "COM_DPHYCONTRX", COM_DPHYCONTRX, A_RW },
+	{ "CLW_CNTRL", CLW_CNTRL, A_RW },
+	{ "D0W_CNTRL", D0W_CNTRL, A_RW },
+	{ "D1W_CNTRL", D1W_CNTRL, A_RW },
+	{ "D2W_CNTRL", D2W_CNTRL, A_RW },
+	{ "D3W_CNTRL", D3W_CNTRL, A_RW },
+	{ "DFTMODE_CNTRL", DFTMODE_CNTRL, A_RW },
+	/* DSI PPI Layer Registers */
+	{ "PPI_STARTPPI", PPI_STARTPPI, A_RW },
+	{ "PPI_BUSYPPI", PPI_BUSYPPI, A_RO },
+	{ "PPI_LINEINITCNT", PPI_LINEINITCNT, A_RW },
+	{ "PPI_LPTXTIMECNT", PPI_LPTXTIMECNT, A_RW },
+	{ "PPI_LANEENABLE", PPI_LANEENABLE, A_RW },
+	{ "PPI_TX_RX_TA", PPI_TX_RX_TA, A_RW },
+	{ "PPI_CLS_ATMR", PPI_CLS_ATMR, A_RW },
+	{ "PPI_D0S_ATMR", PPI_D0S_ATMR, A_RW },
+	{ "PPI_D1S_ATMR", PPI_D1S_ATMR, A_RW },
+	{ "PPI_D2S_ATMR", PPI_D2S_ATMR, A_RW },
+	{ "PPI_D3S_ATMR", PPI_D3S_ATMR, A_RW },
+	{ "PPI_D0S_CLRSIPOCOUNT", PPI_D0S_CLRSIPOCOUNT, A_RW },
+	{ "PPI_D1S_CLRSIPOCOUNT", PPI_D1S_CLRSIPOCOUNT, A_RW },
+	{ "PPI_D2S_CLRSIPOCOUNT", PPI_D2S_CLRSIPOCOUNT, A_RW },
+	{ "PPI_D3S_CLRSIPOCOUNT", PPI_D3S_CLRSIPOCOUNT, A_RW },
+	{ "CLS_PRE", CLS_PRE, A_RW },
+	{ "D0S_PRE", D0S_PRE, A_RW },
+	{ "D1S_PRE", D1S_PRE, A_RW },
+	{ "D2S_PRE", D2S_PRE, A_RW },
+	{ "D3S_PRE", D3S_PRE, A_RW },
+	{ "CLS_PREP", CLS_PREP, A_RW },
+	{ "D0S_PREP", D0S_PREP, A_RW },
+	{ "D1S_PREP", D1S_PREP, A_RW },
+	{ "D2S_PREP", D2S_PREP, A_RW },
+	{ "D3S_PREP", D3S_PREP, A_RW },
+	{ "CLS_ZERO", CLS_ZERO, A_RW },
+	{ "D0S_ZERO", D0S_ZERO, A_RW },
+	{ "D1S_ZERO", D1S_ZERO, A_RW },
+	{ "D2S_ZERO", D2S_ZERO, A_RW  },
+	{ "D3S_ZERO", D3S_ZERO, A_RW },
+	{ "PPI_CLRFLG", PPI_CLRFLG, A_RW },
+	{ "PPI_CLRSIPO", PPI_CLRSIPO, A_RW },
+	{ "PPI_HSTimeout", PPI_HSTimeout, A_RW },
+	{ "PPI_HSTimeoutEnable", PPI_HSTimeoutEnable, A_RW },
+	/* DSI Protocol Layer Registers */
+	{ "DSI_STARTDSI", DSI_STARTDSI, A_WO },
+	{ "DSI_BUSYDSI", DSI_BUSYDSI, A_RO },
+	{ "DSI_LANEENABLE", DSI_LANEENABLE, A_RW },
+	{ "DSI_LANESTATUS0", DSI_LANESTATUS0, A_RO },
+	{ "DSI_LANESTATUS1", DSI_LANESTATUS1, A_RO },
+	{ "DSI_INTSTATUS", DSI_INTSTATUS, A_RO },
+	{ "DSI_INTMASK", DSI_INTMASK, A_RW },
+	{ "DSI_INTCLR", DSI_INTCLR, A_WO },
+	{ "DSI_LPTXTO", DSI_LPTXTO, A_RW },
+	/* DSI General Registers */
+	{ "DSIERRCNT", DSIERRCNT, A_RW },
+	/* DSI Application Layer Registers */
+	{ "APLCTRL", APLCTRL, A_RW },
+	{ "RDPKTLN", RDPKTLN, A_RW },
+	/* Video Path Registers */
+	{ "VPCTRL", VPCTRL, A_RW },
+	{ "HTIM1", HTIM1, A_RW },
+	{ "HTIM2",  HTIM2, A_RW },
+	{ "VTIM1", VTIM1, A_RW },
+	{ "VTIM2", VTIM2, A_RW },
+	{ "VFUEN", VFUEN, A_RW },
+	/* LVDS Registers */
+	{ "LVMX0003", LVMX0003, A_RW },
+	{ "LVMX0407", LVMX0407, A_RW },
+	{ "LVMX0811", LVMX0811, A_RW },
+	{ "LVMX1215", LVMX1215, A_RW },
+	{ "LVMX1619", LVMX1619, A_RW },
+	{ "LVMX2023", LVMX2023, A_RW },
+	{ "LVMX2427", LVMX2427, A_RW },
+	{ "LVCFG", LVCFG, A_RW },
+	{ "LVPHY0", LVPHY0, A_RW },
+	{ "LVPHY1", LVPHY1, A_RW },
+	/* System Registers */
+	{ "SYSSTAT", SYSSTAT, A_RO },
+	{ "SYSRST", SYSRST, A_WO },
+	/* GPIO Registers */
+	{ "GPIOC", GPIOC, A_RW },
+	{ "GPIOO", GPIOO, A_RW },
+	{ "GPIOI", GPIOI, A_RO },
+	/* I2C Registers */
+	{ "I2CTIMCTRL", I2CTIMCTRL, A_RW },
+	{ "I2CMADDR", I2CMADDR, A_RW },
+	{ "WDATAQ", WDATAQ, A_WO },
+	{ "RDATAQ", RDATAQ, A_WO },
+	/* Chip/Rev Registers */
+	{ "IDREG", IDREG, A_RO },
+	/* Debug Registers */
+	{ "DEBUG00", DEBUG00, A_RW },
+	{ "DEBUG01", DEBUG01, A_RW },
+};
+#endif
+
+static int tc358765_read_block(u16 reg, u8 *data, int len)
+{
+	unsigned char wb[2];
+	struct i2c_msg msg[2];
+	int r;
+	mutex_lock(&tc358765_i2c->xfer_lock);
+	wb[0] = (reg & 0xff00) >> 8;
+	wb[1] = reg & 0xff;
+	msg[0].addr = tc358765_i2c->client->addr;
+	msg[0].len = 2;
+	msg[0].flags = 0;
+	msg[0].buf = wb;
+	msg[1].addr = tc358765_i2c->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = data;
+
+	r = i2c_transfer(tc358765_i2c->client->adapter, msg, ARRAY_SIZE(msg));
+	mutex_unlock(&tc358765_i2c->xfer_lock);
+
+	if (r = ARRAY_SIZE(msg))
+		return len;
+
+	return r;
+}
+
+static int tc358765_i2c_read(u16 reg, u32 *val)
+{
+	int r;
+	u8 data[4];
+	data[0] = data[1] = data[2] = data[3] = 0;
+
+	r = tc358765_read_block(reg, data, ARRAY_SIZE(data));
+	if (r != ARRAY_SIZE(data))
+		return r;
+
+	*val = ((int)data[3] << 24) | ((int)(data[2]) << 16) |
+	    ((int)(data[1]) << 8) | ((int)(data[0]));
+	return 0;
+}
+
+static int tc358765_dsi_read(struct omap_dss_device *dssdev, u16 reg, u32 *val)
+{
+	struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+	u8 buf[4];
+	int r;
+
+	r = dsi_vc_generic_read_2(dssdev, d2d->channel1, ((u8 *)&reg)[0],
+				((u8 *)&reg)[1], buf, 4);
+	if (r < 0) {
+		dev_err(&dssdev->dev, "0x%x read failed with status %d\n",
+								reg, r);
+		return r;
+	}
+
+	*val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+	return 0;
+}
+
+static int tc358765_read_register(struct omap_dss_device *dssdev,
+					u16 reg, u32 *val)
+{
+	int ret = 0;
+	pm_runtime_get_sync(&dssdev->dev);
+	/* I2C is preferred way of reading, but fall back to DSI
+	 * if I2C didn't got initialized
+	*/
+	if (tc358765_i2c)
+		ret = tc358765_i2c_read(reg, val);
+	else
+		ret = tc358765_dsi_read(dssdev, reg, val);
+	pm_runtime_put_sync(&dssdev->dev);
+
+	return ret;
+}
+
+static int tc358765_write_register(struct omap_dss_device *dssdev, u16 reg,
+		u32 value)
+{
+	struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+	u8 buf[6];
+	int r;
+
+	buf[0] = (reg >> 0) & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+	buf[2] = (value >> 0) & 0xff;
+	buf[3] = (value >> 8) & 0xff;
+	buf[4] = (value >> 16) & 0xff;
+	buf[5] = (value >> 24) & 0xff;
+
+	r = dsi_vc_generic_write_nosync(dssdev, d2d->channel1, buf, 6);
+	if (r)
+		dev_err(&dssdev->dev, "reg write reg(%x) val(%x) failed: %d\n",
+			       reg, value, r);
+	return r;
+}
+
+/****************************
+********* DEBUG *************
+****************************/
+#ifdef CONFIG_TC358765_DEBUG
+static int tc358765_write_register_i2c(u16 reg, u32 val)
+{
+	int ret = -ENODEV;
+	unsigned char buf[6];
+	struct i2c_msg msg;
+
+	if (!tc358765_i2c) {
+		dev_err(tc358765_debug.dev, "%s: I2C not initilized\n",
+							__func__);
+		return ret;
+	}
+
+	buf[0] = (reg >> 8) & 0xff;
+	buf[1] = (reg >> 0) & 0xff;
+	buf[2] = (val >> 0) & 0xff;
+	buf[3] = (val >> 8) & 0xff;
+	buf[4] = (val >> 16) & 0xff;
+	buf[5] = (val >> 24) & 0xff;
+	msg.addr = tc358765_i2c->client->addr;
+	msg.len = sizeof(buf);
+	msg.flags = 0;
+	msg.buf = buf;
+
+	mutex_lock(&tc358765_i2c->xfer_lock);
+	ret = i2c_transfer(tc358765_i2c->client->adapter, &msg, 1);
+	mutex_unlock(&tc358765_i2c->xfer_lock);
+
+	if (ret != 1)
+		return ret;
+	return 0;
+}
+
+
+static int tc358765_registers_show(struct seq_file *seq, void *pos)
+{
+	struct device *dev = tc358765_debug.dev;
+	unsigned i, reg_count;
+	uint value;
+
+	if (!tc358765_i2c) {
+		dev_warn(dev,
+			"failed to read register: I2C not initialized\n");
+		return -ENODEV;
+	}
+
+	reg_count = sizeof(tc358765_regs) / sizeof(tc358765_regs[0]);
+	pm_runtime_get_sync(dev);
+	for (i = 0; i < reg_count; i++) {
+		if (tc358765_regs[i].perm & A_RO) {
+			tc358765_i2c_read(tc358765_regs[i].reg, &value);
+			seq_printf(seq, "%-20s = 0x%02X\n",
+					tc358765_regs[i].name, value);
+		}
+	}
+
+	pm_runtime_put_sync(dev);
+	return 0;
+}
+static ssize_t tc358765_seq_write(struct file *filp, const char __user *ubuf,
+						size_t size, loff_t *ppos)
+{
+	struct device *dev = tc358765_debug.dev;
+	unsigned i, reg_count;
+	u32 value = 0;
+	int error = 0;
+	/* kids, don't use register names that long */
+	char name[30];
+	char buf[50];
+
+	if (size >= sizeof(buf))
+		size = sizeof(buf);
+
+	if (copy_from_user(&buf, ubuf, size))
+		return -EFAULT;
+
+	buf[size-1] = '\0';
+	if (sscanf(buf, "%s %x", name, &value) != 2) {
+		dev_err(dev, "%s: unable to parse input\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!tc358765_i2c) {
+		dev_warn(dev,
+			"failed to write register: I2C not initialized\n");
+		return -ENODEV;
+	}
+
+	reg_count = sizeof(tc358765_regs) / sizeof(tc358765_regs[0]);
+	for (i = 0; i < reg_count; i++) {
+		if (!strcmp(name, tc358765_regs[i].name)) {
+			if (!(tc358765_regs[i].perm & A_WO)) {
+				dev_err(dev, "%s is write-protected\n", name);
+				return -EACCES;
+			}
+
+			error = tc358765_write_register_i2c(
+				tc358765_regs[i].reg, value);
+			if (error) {
+				dev_err(dev, "%s: failed to write %s\n",
+					__func__, name);
+				return error;
+			}
+
+			return size;
+		}
+	}
+
+	dev_err(dev, "%s: no such register %s\n", __func__, name);
+
+	return size;
+}
+
+static ssize_t tc358765_seq_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tc358765_registers_show, inode->i_private);
+}
+
+static const struct file_operations tc358765_debug_fops = {
+	.open		= tc358765_seq_open,
+	.read		= seq_read,
+	.write		= tc358765_seq_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int tc358765_initialize_debugfs(struct omap_dss_device *dssdev)
+{
+	tc358765_debug.dir = debugfs_create_dir("tc358765", NULL);
+	if (IS_ERR(tc358765_debug.dir)) {
+		int ret = PTR_ERR(tc358765_debug.dir);
+		tc358765_debug.dir = NULL;
+		return ret;
+	}
+
+	tc358765_debug.dev = &dssdev->dev;
+	debugfs_create_file("registers", S_IRWXU, tc358765_debug.dir,
+			dssdev, &tc358765_debug_fops);
+	return 0;
+}
+
+static void tc358765_uninitialize_debugfs(void)
+{
+	if (tc358765_debug.dir)
+		debugfs_remove_recursive(tc358765_debug.dir);
+	tc358765_debug.dir = NULL;
+	tc358765_debug.dev = NULL;
+}
+
+#else
+static int tc358765_initialize_debugfs(struct omap_dss_device *dssdev)
+{
+	return 0;
+}
+
+static void tc358765_uninitialize_debugfs(void)
+{
+}
+#endif
+
+static struct tc358765_board_data *get_board_data(struct omap_dss_device
+								*dssdev)
+{
+	return (struct tc358765_board_data *)dssdev->data;
+}
+
+static void tc358765_get_timings(struct omap_dss_device *dssdev,
+		struct omap_video_timings *timings)
+{
+	*timings = dssdev->panel.timings;
+}
+
+static void tc358765_set_timings(struct omap_dss_device *dssdev,
+		struct omap_video_timings *timings)
+{
+	dev_info(&dssdev->dev, "set_timings() not implemented\n");
+}
+
+static int tc358765_check_timings(struct omap_dss_device *dssdev,
+		struct omap_video_timings *timings)
+{
+	if (unlikely(!timings)) {
+		WARN(true, "%s: timings NULL pointer was passed\n", __func__);
+		return -EINVAL;
+	}
+
+	if (tc358765_timings.x_res != timings->x_res ||
+			tc358765_timings.y_res != timings->y_res ||
+			tc358765_timings.pixel_clock != timings->pixel_clock ||
+			tc358765_timings.hsw != timings->hsw ||
+			tc358765_timings.hfp != timings->hfp ||
+			tc358765_timings.hbp != timings->hbp ||
+			tc358765_timings.vsw != timings->vsw ||
+			tc358765_timings.vfp != timings->vfp ||
+			tc358765_timings.vbp != timings->vbp)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void tc358765_get_resolution(struct omap_dss_device *dssdev,
+		u16 *xres, u16 *yres)
+{
+	*xres = tc358765_timings.x_res;
+	*yres = tc358765_timings.y_res;
+}
+
+static void tc358765_hw_reset(struct omap_dss_device *dssdev)
+{
+
+	if (dssdev = NULL || !gpio_is_valid(dssdev->reset_gpio))
+		return;
+
+	gpio_set_value(dssdev->reset_gpio, 1);
+	usleep_range(200, 1000);
+	/* reset the panel */
+	gpio_set_value(dssdev->reset_gpio, 0);
+	/* assert reset */
+	usleep_range(200, 1000);
+	gpio_set_value(dssdev->reset_gpio, 1);
+	/* wait after releasing reset */
+	msleep(200);
+
+	return;
+}
+
+static void tc358765_probe_pdata(struct tc358765_data *d2d,
+		const struct tc358765_board_data *pdata)
+{
+	d2d->pin_config = pdata->pin_config;
+}
+
+static int tc358765_probe(struct omap_dss_device *dssdev)
+{
+	struct tc358765_data *d2d;
+	int r = 0;
+
+	dev_dbg(&dssdev->dev, "tc358765_probe\n");
+
+	dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
+	tc358765_timings = dssdev->panel.timings;
+
+	d2d = devm_kzalloc(&dssdev->dev, sizeof(*d2d), GFP_KERNEL);
+	if (!d2d) {
+		r = -ENOMEM;
+		goto err;
+	}
+
+	d2d->dssdev = dssdev;
+
+	mutex_init(&d2d->lock);
+
+	dev_set_drvdata(&dssdev->dev, d2d);
+
+	if (dssdev->data) {
+		const struct tc358765_board_data *pdata = dssdev->data;
+
+		tc358765_probe_pdata(d2d, pdata);
+	} else {
+		return -ENODEV;
+	}
+
+	/* channel0 used for video packets */
+	r = omap_dsi_request_vc(dssdev, &d2d->channel0);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to get virtual channel0\n");
+		goto err;
+	}
+
+	r = omap_dsi_set_vc_id(dssdev, d2d->channel0, 0);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to set VC_ID0\n");
+		goto err_ch0;
+	}
+
+	/* channel1 used for registers access in LP mode */
+	r = omap_dsi_request_vc(dssdev, &d2d->channel1);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to get virtual channel1\n");
+		goto err_ch0;
+	}
+
+	r = omap_dsi_set_vc_id(dssdev, d2d->channel1, 0);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to set VC_ID1\n");
+		goto err_ch1;
+	}
+	r = tc358765_initialize_debugfs(dssdev);
+	if (r)
+		dev_warn(&dssdev->dev, "failed to create sysfs files\n");
+
+	dev_dbg(&dssdev->dev, "tc358765_probe done\n");
+	return 0;
+
+err_ch1:
+	omap_dsi_release_vc(dssdev, d2d->channel1);
+err_ch0:
+	omap_dsi_release_vc(dssdev, d2d->channel0);
+err:
+	mutex_destroy(&d2d->lock);
+	devm_kfree(&dssdev->dev, d2d);
+	return r;
+}
+
+static void tc358765_remove(struct omap_dss_device *dssdev)
+{
+	struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+
+	tc358765_uninitialize_debugfs();
+
+	omap_dsi_release_vc(dssdev, d2d->channel0);
+	omap_dsi_release_vc(dssdev, d2d->channel1);
+	mutex_destroy(&d2d->lock);
+	devm_kfree(&dssdev->dev, d2d);
+}
+
+static int tc358765_init_ppi(struct omap_dss_device *dssdev)
+{
+	u32 go_cnt, sure_cnt, val = 0;
+	u8 lanes = 0;
+	int ret = 0;
+	struct tc358765_board_data *board_data = get_board_data(dssdev);
+	const int *pins = board_data->pin_config.pins;
+
+	/*
+	 * This register setting is required only if host wishes to
+	 * perform DSI read transactions
+	 */
+	go_cnt = (board_data->lp_time * 5 - 3) / 4;
+	sure_cnt = DIV_ROUND_UP(board_data->lp_time * 3, 2);
+	val = FLD_MOD(val, go_cnt, 26, 16);
+	val = FLD_MOD(val, sure_cnt, 10, 0);
+	ret |= tc358765_write_register(dssdev, PPI_TX_RX_TA, val);
+
+	/* SYSLPTX Timing Generation Counter */
+	ret |= tc358765_write_register(dssdev, PPI_LPTXTIMECNT,
+					board_data->lp_time);
+
+	/* D*S_CLRSIPOCOUNT = [(THS-SETTLE + THS-ZERO) /
+					HS_byte_clock_period ] */
+
+	if ((pins[0] & 1) || (pins[1] & 1))
+		lanes |= (1 << 0);
+
+	if ((pins[2] & 1) || (pins[3] & 1)) {
+		lanes |= (1 << 1);
+		ret |= tc358765_write_register(dssdev, PPI_D0S_CLRSIPOCOUNT,
+							board_data->clrsipo);
+	}
+	if ((pins[4] & 1) || (pins[5] & 1)) {
+		lanes |= (1 << 2);
+		ret |= tc358765_write_register(dssdev, PPI_D1S_CLRSIPOCOUNT,
+							board_data->clrsipo);
+	}
+	if ((pins[6] & 1) || (pins[7] & 1)) {
+		lanes |= (1 << 3);
+		ret |= tc358765_write_register(dssdev, PPI_D2S_CLRSIPOCOUNT,
+							board_data->clrsipo);
+	}
+	if ((pins[8] & 1) || (pins[9] & 1)) {
+		lanes |= (1 << 4);
+		ret |= tc358765_write_register(dssdev, PPI_D3S_CLRSIPOCOUNT,
+							board_data->clrsipo);
+	}
+
+	ret |= tc358765_write_register(dssdev, PPI_LANEENABLE, lanes);
+	ret |= tc358765_write_register(dssdev, DSI_LANEENABLE, lanes);
+
+	return ret;
+}
+
+static int tc358765_init_video_timings(struct omap_dss_device *dssdev)
+{
+	u32 val;
+	struct tc358765_board_data *board_data = get_board_data(dssdev);
+	int ret;
+	ret = tc358765_read_register(dssdev, VPCTRL, &val);
+	if (ret < 0) {
+		dev_warn(&dssdev->dev,
+			"couldn't access VPCTRL, going on with reset value\n");
+		val = 0;
+	}
+
+	if (dssdev->ctrl.pixel_size = 18) {
+		/* Magic Square FRC available for RGB666 only */
+		val = FLD_MOD(val, board_data->msf, 0, 0);
+		val = FLD_MOD(val, 0, 8, 8);
+	} else {
+		val = FLD_MOD(val, 1, 8, 8);
+	}
+
+	val = FLD_MOD(val, board_data->vtgen, 4, 4);
+	val = FLD_MOD(val, board_data->evtmode, 5, 5);
+	val = FLD_MOD(val, board_data->vsdelay, 31, 20);
+
+	ret = tc358765_write_register(dssdev, VPCTRL, val);
+
+	ret |= tc358765_write_register(dssdev, HTIM1,
+		(tc358765_timings.hbp << 16) | tc358765_timings.hsw);
+	ret |= tc358765_write_register(dssdev, HTIM2,
+		((tc358765_timings.hfp << 16) | tc358765_timings.x_res));
+	ret |= tc358765_write_register(dssdev, VTIM1,
+		((tc358765_timings.vbp << 16) |	tc358765_timings.vsw));
+	ret |= tc358765_write_register(dssdev, VTIM2,
+		((tc358765_timings.vfp << 16) |	tc358765_timings.y_res));
+	return ret;
+}
+
+static int tc358765_write_init_config(struct omap_dss_device *dssdev)
+{
+	struct tc358765_board_data *board_data = get_board_data(dssdev);
+	u32 val;
+	int r;
+
+	/* HACK: dummy read: if we read via DSI, first reads always fail */
+	tc358765_read_register(dssdev, DSI_INTSTATUS, &val);
+
+	r = tc358765_init_ppi(dssdev);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to initialize PPI layer\n");
+		return r;
+	}
+
+	r = tc358765_write_register(dssdev, PPI_STARTPPI, 0x1);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to start PPI-TX\n");
+		return r;
+	}
+
+	r = tc358765_write_register(dssdev, DSI_STARTDSI, 0x1);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to start DSI-RX\n");
+		return r;
+	}
+
+	/* reset LVDS-PHY */
+	tc358765_write_register(dssdev, LVPHY0, (1 << 22));
+	usleep_range(2000, 3000);
+
+	r = tc358765_read_register(dssdev, LVPHY0, &val);
+	if (r < 0) {
+		dev_warn(&dssdev->dev, "couldn't access LVPHY0, going on with reset value\n");
+		val = 0;
+	}
+	val = FLD_MOD(val, 0, LV_RST_E, LV_RST_B);
+	val = FLD_MOD(val, board_data->lv_is, LV_IS_E, LV_IS_B);
+	val = FLD_MOD(val, board_data->lv_nd, LV_ND_E, LV_ND_B);
+	r = tc358765_write_register(dssdev, LVPHY0, val);
+
+	if (r) {
+		dev_err(&dssdev->dev, "failed to initialize LVDS-PHY\n");
+		return r;
+	}
+
+	r = tc358765_init_video_timings(dssdev);
+
+	if (r) {
+		dev_err(&dssdev->dev, "failed to initialize video path layer\n");
+		return r;
+	}
+
+	r = tc358765_read_register(dssdev, LVCFG, &val);
+	if (r < 0) {
+		dev_warn(&dssdev->dev,
+			"couldn't access LVCFG, going on with reset value\n");
+		val = 0;
+	}
+
+	val = FLD_MOD(val, board_data->pclkdiv, 9, 8);
+	val = FLD_MOD(val, board_data->pclksel, 11, 10);
+	val = FLD_MOD(val, board_data->lvdlink, 1, 1);
+	/* enable LVDS transmitter */
+	val = FLD_MOD(val, 1, 0, 0);
+	r = tc358765_write_register(dssdev, LVCFG, val);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to start LVDS transmitter\n");
+		return r;
+	}
+
+	/* Issue a soft reset to LCD Controller for a clean start */
+	r = tc358765_write_register(dssdev, SYSRST, (1 << 2));
+	/* commit video configuration */
+	r |= tc358765_write_register(dssdev, VFUEN, 0x1);
+	if (r)
+		dev_err(&dssdev->dev, "failed to latch video timings\n");
+	return r;
+}
+
+static int tc358765_power_on(struct omap_dss_device *dssdev)
+{
+	struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+	int r;
+
+	/* At power on the first vsync has not been received yet */
+
+	dev_dbg(&dssdev->dev, "power_on\n");
+
+	if (dssdev->platform_enable)
+		dssdev->platform_enable(dssdev);
+
+	r = omapdss_dsi_configure_pins(dssdev, &d2d->pin_config);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to configure DSI pins\n");
+		goto err_disp_enable;
+	}
+
+	omapdss_dsi_set_size(dssdev, dssdev->panel.timings.x_res,
+					dssdev->panel.timings.y_res);
+	omapdss_dsi_set_pixel_format(dssdev, dssdev->panel.dsi_pix_fmt);
+	omapdss_dsi_set_operation_mode(dssdev, dssdev->panel.dsi_mode);
+	omapdss_dsi_set_timings(dssdev, &dssdev->panel.timings);
+	omapdss_dsi_set_videomode_timings(dssdev,
+					&dssdev->panel.dsi_vm_timings);
+
+	r = omapdss_dsi_set_clocks(dssdev, 187200000, 10000000);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to set HS and LP clocks\n");
+		goto err_disp_enable;
+	}
+
+	r = omapdss_dsi_display_enable(dssdev);
+	if (r) {
+		dev_err(&dssdev->dev, "failed to enable DSI\n");
+		goto err_disp_enable;
+	}
+
+	/* reset tc358765 bridge */
+	tc358765_hw_reset(dssdev);
+
+	/*turn on HS clock to bring up bridge i2c slave */
+	omapdss_dsi_vc_enable_hs(dssdev, d2d->channel0, true);
+
+	/* configure D2L chip DSI-RX configuration registers */
+
+	r = tc358765_write_init_config(dssdev);
+	if (r)
+		goto err_write_init;
+
+	r = dsi_enable_video_output(dssdev, d2d->channel0);
+
+	dev_dbg(&dssdev->dev, "power_on done\n");
+
+	return r;
+
+err_write_init:
+	omapdss_dsi_display_disable(dssdev, false, false);
+err_disp_enable:
+	if (dssdev->platform_disable)
+		dssdev->platform_disable(dssdev);
+
+	return r;
+}
+
+static void tc358765_power_off(struct omap_dss_device *dssdev)
+{
+	struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+
+	dsi_disable_video_output(dssdev, d2d->channel0);
+	dsi_disable_video_output(dssdev, d2d->channel1);
+
+	omapdss_dsi_display_disable(dssdev, false, false);
+
+	if (dssdev->platform_disable)
+		dssdev->platform_disable(dssdev);
+}
+
+static void tc358765_disable(struct omap_dss_device *dssdev)
+{
+	struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+
+	dev_dbg(&dssdev->dev, "disable\n");
+
+	if (dssdev->state = OMAP_DSS_DISPLAY_ACTIVE) {
+		mutex_lock(&d2d->lock);
+		dsi_bus_lock(dssdev);
+
+		tc358765_power_off(dssdev);
+
+		dsi_bus_unlock(dssdev);
+		mutex_unlock(&d2d->lock);
+	}
+	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static int tc358765_enable(struct omap_dss_device *dssdev)
+{
+	struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+	int r = 0;
+
+	dev_dbg(&dssdev->dev, "enable\n");
+
+	if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
+		return -EINVAL;
+
+	mutex_lock(&d2d->lock);
+	dsi_bus_lock(dssdev);
+
+	r = tc358765_power_on(dssdev);
+
+	dsi_bus_unlock(dssdev);
+
+	if (r) {
+		dev_dbg(&dssdev->dev, "enable failed\n");
+		dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+	} else {
+		dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+	}
+
+	mutex_unlock(&d2d->lock);
+
+	return r;
+}
+
+static struct omap_dss_driver tc358765_driver = {
+	.probe		= tc358765_probe,
+	.remove		= tc358765_remove,
+
+	.enable		= tc358765_enable,
+	.disable	= tc358765_disable,
+
+	.get_resolution	= tc358765_get_resolution,
+	.get_recommended_bpp = omapdss_default_get_recommended_bpp,
+
+	.get_timings	= tc358765_get_timings,
+	.set_timings	= tc358765_set_timings,
+	.check_timings	= tc358765_check_timings,
+
+	.driver         = {
+		.name   = "tc358765",
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int tc358765_i2c_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	tc358765_i2c = devm_kzalloc(&client->dev, sizeof(*tc358765_i2c), GFP_KERNEL);
+	if (tc358765_i2c = NULL)
+		return -ENOMEM;
+
+	/* store i2c_client pointer on private data structure */
+	tc358765_i2c->client = client;
+
+	/* store private data structure pointer on i2c_client structure */
+	i2c_set_clientdata(client, tc358765_i2c);
+
+	/* init mutex */
+	mutex_init(&tc358765_i2c->xfer_lock);
+	dev_err(&client->dev, "D2L i2c initialized\n");
+
+	return 0;
+}
+
+/* driver remove function */
+static int __exit tc358765_i2c_remove(struct i2c_client *client)
+{
+	/* remove client data */
+	i2c_set_clientdata(client, NULL);
+
+	/* destroy mutex */
+	mutex_destroy(&tc358765_i2c->xfer_lock);
+
+	/* free private data memory */
+	devm_kfree(&client->dev, tc358765_i2c);
+
+	return 0;
+}
+
+static const struct i2c_device_id tc358765_i2c_idtable[] = {
+	{"tc358765_i2c_driver", 0},
+	{},
+};
+
+static struct i2c_driver tc358765_i2c_driver = {
+	.probe = tc358765_i2c_probe,
+	.remove = __exit_p(tc358765_i2c_remove),
+	.id_table = tc358765_i2c_idtable,
+	.driver = {
+		   .name  = "d2l",
+		   .owner = THIS_MODULE,
+	},
+};
+
+
+static int __init tc358765_init(void)
+{
+	int r;
+	tc358765_i2c = NULL;
+	r = i2c_add_driver(&tc358765_i2c_driver);
+	if (r < 0) {
+		printk(KERN_WARNING "d2l i2c driver registration failed\n");
+		return r;
+	}
+
+	omap_dss_register_driver(&tc358765_driver);
+	return 0;
+}
+
+static void __exit tc358765_exit(void)
+{
+	omap_dss_unregister_driver(&tc358765_driver);
+	i2c_del_driver(&tc358765_i2c_driver);
+}
+
+module_init(tc358765_init);
+module_exit(tc358765_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("TC358765 DSI-2-LVDS Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-tc358765.h b/drivers/video/omap2/displays/panel-tc358765.h
new file mode 100644
index 0000000..ffc105d
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-tc358765.h
@@ -0,0 +1,170 @@
+/*
+ * Header for DSI-to-LVDS bridge driver
+ *
+ * Copyright (C) 2012 Texas Instruments Inc
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ * Author: Sergii Kibrik <sergiikibrik@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PANEL_TC358765_H__
+#define __PANEL_TC358765_H__
+
+/* DSI D-PHY Layer Registers */
+#define	D0W_DPHYCONTTX		0x0004		/* Data Lane 0 DPHY TX */
+#define	CLW_DPHYCONTRX		0x0020		/* Clock Lane DPHY RX */
+#define	D0W_DPHYCONTRX		0x0024		/* Data Land 0 DPHY Rx */
+#define	D1W_DPHYCONTRX		0x0028		/* Data Lane 1 DPHY Rx */
+#define	D2W_DPHYCONTRX		0x002c		/* Data Lane 2 DPHY Rx */
+#define	D3W_DPHYCONTRX		0x0030		/* Data Lane 3 DPHY Rx */
+#define	COM_DPHYCONTRX		0x0038		/* DPHY Rx Common */
+#define	CLW_CNTRL		0x0040		/* Clock Lane */
+#define	D0W_CNTRL		0x0044		/* Data Lane 0 */
+#define	D1W_CNTRL		0x0048		/* Data Lane 1 */
+#define	D2W_CNTRL		0x004c		/* Data Lane 2 */
+#define	D3W_CNTRL		0x0050		/* Data Lane 3 */
+#define	DFTMODE_CNTRL		0x0054		/* DFT Mode */
+
+/* DSI PPI Layer Registers */
+#define	PPI_STARTPPI		0x0104		/* Start control bit */
+#define	PPI_BUSYPPI		0x0108			/* Busy bit */
+#define	PPI_LINEINITCNT		0x0110		/* Line In initialization */
+#define	PPI_LPTXTIMECNT		0x0114		/* LPTX timing signal */
+#define	PPI_LANEENABLE		0x0134		/* Lane Enable */
+#define	PPI_TX_RX_TA		0x013c		/* BTA timing param */
+#define	PPI_CLS_ATMR		0x0140		/* Analog timer fcn */
+#define	PPI_D0S_ATMR		0x0144		/* Analog timer fcn Lane 0 */
+#define	PPI_D1S_ATMR		0x0148		/* Analog timer fcn Lane 1 */
+#define	PPI_D2S_ATMR		0x014c		/* Analog timer fcn Lane 2 */
+#define	PPI_D3S_ATMR		0x0150		/* Analog timer fcn Lane 3 */
+#define	PPI_D0S_CLRSIPOCOUNT	0x0164		/* Assertion timer Lane 0 */
+#define	PPI_D1S_CLRSIPOCOUNT	0x0168		/* Assertion timer Lane 1 */
+#define	PPI_D2S_CLRSIPOCOUNT	0x016c		/* Assertion timer Lane 1 */
+#define	PPI_D3S_CLRSIPOCOUNT	0x0170		/* Assertion timer Lane 1 */
+#define CLS_PRE			0x0180		/* PHY IO cntr */
+#define D0S_PRE			0x0184		/* PHY IO cntr */
+#define D1S_PRE			0x0188		/* PHY IO cntr */
+#define D2S_PRE			0x018c		/* PHY IO cntr */
+#define D3S_PRE			0x0190		/* PHY IO cntr */
+#define CLS_PREP		0x01a0		/* PHY IO cntr */
+#define D0S_PREP		0x01a4		/* PHY IO cntr */
+#define D1S_PREP		0x01a8		/* PHY IO cntr */
+#define D2S_PREP		0x01ac		/* PHY IO cntr */
+#define D3S_PREP		0x01b0		/* PHY IO cntr */
+#define CLS_ZERO		0x01c0		/* PHY IO cntr */
+#define	D0S_ZERO		0x01c4		/* PHY IO cntr */
+#define	D1S_ZERO		0x01c8		/* PHY IO cntr */
+#define	D2S_ZERO		0x01cc		/* PHY IO cntr */
+#define	D3S_ZERO		0x01d0		/* PHY IO cntr */
+#define PPI_CLRFLG		0x01e0		/* PRE cntrs */
+#define PPI_CLRSIPO		0x01e4		/* Clear SIPO */
+#define PPI_HSTimeout		0x01f0		/* HS RX timeout */
+#define PPI_HSTimeoutEnable	0x01f4		/* Enable HS Rx Timeout */
+
+/* DSI Protocol Layer Registers */
+#define DSI_STARTDSI		0x0204		/* DSI TX start bit */
+#define DSI_BUSYDSI		0x0208		/* DSI busy bit */
+#define DSI_LANEENABLE		0x0210		/* Lane enable */
+#define DSI_LANESTATUS0		0x0214		/* HS Rx mode */
+#define DSI_LANESTATUS1		0x0218		/* ULPS or STOP state */
+#define DSI_INTSTATUS		0x0220		/* Interrupt status */
+#define DSI_INTMASK		0x0224		/* Interrupt mask */
+#define DSI_INTCLR		0x0228		/* Interrupt clear */
+#define DSI_LPTXTO		0x0230		/* LP Tx Cntr */
+
+/* DSI General Registers */
+#define	DSIERRCNT		0x0300		/* DSI Error Count */
+
+/* DSI Application Layer Registers */
+#define APLCTRL			0x0400		/* Application Layer Cntrl */
+#define RDPKTLN			0x0404		/* Packet length */
+
+/* Video Path Registers */
+#define	VPCTRL			0x0450		/* Video Path */
+#define HTIM1			0x0454		/* Horizontal Timing */
+#define HTIM2			0x0458		/* Horizontal Timing */
+#define VTIM1			0x045c		/* Vertical Timing */
+#define VTIM2			0x0460		/* Vertical Timing */
+#define VFUEN			0x0464		/* Video Frame Timing */
+
+
+/* LVDS Registers - LVDS Mux Input */
+#define LVMX0003		0x0480		/* Bit 0 to 3*/
+#define LVMX0407		0x0484		/* Bit 4 to 7 */
+#define LVMX0811		0x0488		/* Bit 8 to 11 */
+#define LVMX1215		0x048c		/* Bit 12 to 15 */
+#define LVMX1619		0x0490		/* Bit 16 to 19 */
+#define LVMX2023		0x0494		/* Bit 20 to 23 */
+#define LVMX2427		0x0498		/* Bit 24 to 27 */
+
+#define LVCFG			0x049c		/* LVDS Config */
+#define	LVPHY0			0x04a0		/* LVDS PHY Reg 0 */
+#define	LVPHY1			0x04a1		/* LVDS PHY Reg 1 */
+
+/* LVDS PHY Register 0 (LVPHY0) entries */
+#define LV_RST_B		22		/* LV PHY reset */
+#define LV_RST_E		22
+#define LV_IS_B			14		/* Charge pump current control */
+#define LV_IS_E			15		/* pin for PLL portion */
+#define LV_ND_B			0		/* Frequency Range Select */
+#define LV_ND_E			4
+
+/* System Registers */
+#define SYSSTAT			0x0500		/* System Status */
+#define SYSRST			0x0504		/* System Reset */
+
+/* GPIO Registers */
+#define GPIOC			0x0520		/* GPIO Control */
+#define GPIOO			0x0520		/* GPIO Output */
+#define GPIOI			0x0520		/* GPIO Input */
+
+/* I2C Registers */
+#define I2CTIMCTRL		0x0540
+#define I2CMADDR		0x0544
+#define WDATAQ			0x0548
+#define RDATAQ			0x054C
+
+/* Chip Revision Registers */
+#define IDREG			0x0580		/* Chip and Revision ID */
+
+/* Debug Register */
+#define DEBUG00			0x05a0		/* Debug */
+#define DEBUG01			0x05a4		/* LVDS Data */
+
+/*DSI DCS commands */
+#define DCS_READ_NUM_ERRORS     0x05
+#define DCS_READ_POWER_MODE     0x0a
+#define DCS_READ_MADCTL         0x0b
+#define DCS_READ_PIXEL_FORMAT   0x0c
+#define DCS_RDDSDR              0x0f
+#define DCS_SLEEP_IN            0x10
+#define DCS_SLEEP_OUT           0x11
+#define DCS_DISPLAY_OFF         0x28
+#define DCS_DISPLAY_ON          0x29
+#define DCS_COLUMN_ADDR         0x2a
+#define DCS_PAGE_ADDR           0x2b
+#define DCS_MEMORY_WRITE        0x2c
+#define DCS_TEAR_OFF            0x34
+#define DCS_TEAR_ON             0x35
+#define DCS_MEM_ACC_CTRL        0x36
+#define DCS_PIXEL_FORMAT        0x3a
+#define DCS_BRIGHTNESS          0x51
+#define DCS_CTRL_DISPLAY        0x53
+#define DCS_WRITE_CABC          0x55
+#define DCS_READ_CABC           0x56
+#define DCS_GET_ID1             0xda
+#define DCS_GET_ID2             0xdb
+#define DCS_GET_ID3             0xdc
+
+#endif
diff --git a/include/video/omap-panel-tc358765.h b/include/video/omap-panel-tc358765.h
new file mode 100644
index 0000000..c58e081
--- /dev/null
+++ b/include/video/omap-panel-tc358765.h
@@ -0,0 +1,53 @@
+/*
+ * Header for DSI-to-LVDS bridge driver
+ *
+ * Copyright (C) 2012 Texas Instruments Inc
+ * Author: Sergii Kibrik <sergiikibrik@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VIDEO_TC358765_BOARD_DATA_H__
+#define __VIDEO_TC358765_BOARD_DATA_H__
+
+/**
+ * struct tc358765_board_data - represent DSI-to-LVDS bridge configuration
+ * @lp_time: Timing Generation Counter
+ * @clrsipo: CLRSIPO counter (one value for all lanes)
+ * @lv_is: charge pump control pin
+ * @lv_nd: Feed Back Divider Ratio
+ * @pclkdiv: PCLK Divide Option
+ * @pclksel: PCLK Selection: HSRCK/HbyteHSClkx2/ByteHsClk
+ * @vsdelay: VSYNC Delay
+ * @lvdlink: is single or dual link
+ * @vtgen: drive video timing signals by the on-chip Video Timing Gen module
+ * @msf: enable/disable Magic Square
+ * @evtmode: event/pulse mode of video timing information transmission
+ * @pin_config: DSI pin configuration
+*/
+struct tc358765_board_data {
+	u16	lp_time;
+	u8	clrsipo;
+	u8	lv_is;
+	u8	lv_nd;
+	u8	pclkdiv;
+	u8	pclksel;
+	u16	vsdelay;
+	bool	lvdlink;
+	bool	vtgen;
+	bool	msf;
+	bool	evtmode;
+	struct omap_dsi_pin_config pin_config;
+};
+
+#endif
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 0/5] at91: atmel_lcdfb: regression fixes and cpu_is removal
From: Nicolas Ferre @ 2013-02-08 16:35 UTC (permalink / raw)
  To: linux-arm-kernel

These patches fix a regression in 16-bpp support for older SOCs which use
IBGR:555 rather than BGR:565 pixel layout. Use SOC-type to determine if the
controller uses the intensity-bit and restore the old layout in that case.

The last patch is a removal of uses of cpu_is_xxxx() macros in atmel_lcdfb with
a platform-device-id table and static configurations.


Patches from Johan Hovold taken from:
"[PATCH 0/3] atmel_lcdfb: fix 16-bpp regression"
and
"[PATCH v2 0/3] ARM: at91/avr32/atmel_lcdfb: remove cpu_is macros"
patch series to form a clean patch series with my signature.

Arnd, Olof,
as it seems that old fbdev drivers are not so much reviewed those days, can we
take the decision to queue this material through arm-soc with other AT91
drivers updates?

Best regards,

Johan Hovold (5):
  atmel_lcdfb: fix 16-bpp modes on older SOCs
  ARM: at91/neocore926: fix LCD-wiring mode
  ARM: at91/avr32/atmel_lcdfb: add bus-clock entry
  atmel_lcdfb: move lcdcon2 register access to compute_hozval
  ARM: at91/avr32/atmel_lcdfb: add platform device-id table

 arch/arm/mach-at91/at91sam9261.c         |   2 +
 arch/arm/mach-at91/at91sam9261_devices.c |   6 +-
 arch/arm/mach-at91/at91sam9263.c         |   1 +
 arch/arm/mach-at91/at91sam9263_devices.c |   2 +-
 arch/arm/mach-at91/at91sam9g45.c         |   2 +
 arch/arm/mach-at91/at91sam9g45_devices.c |   6 +-
 arch/arm/mach-at91/at91sam9rl.c          |   1 +
 arch/arm/mach-at91/at91sam9rl_devices.c  |   2 +-
 arch/arm/mach-at91/board-neocore926.c    |   2 +-
 arch/avr32/mach-at32ap/at32ap700x.c      |   6 +-
 drivers/video/atmel_lcdfb.c              | 130 ++++++++++++++++++++++++-------
 include/video/atmel_lcdc.h               |   4 +-
 12 files changed, 127 insertions(+), 37 deletions(-)

-- 
1.8.0


^ permalink raw reply

* [PATCH 1/5] atmel_lcdfb: fix 16-bpp modes on older SOCs
From: Nicolas Ferre @ 2013-02-08 16:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1360340791.git.nicolas.ferre@atmel.com>

From: Johan Hovold <jhovold@gmail.com>

Fix regression introduced by commit 787f9fd23283 ("atmel_lcdfb: support
16bit BGR:565 mode, remove unsupported 15bit modes") which broke 16-bpp
modes for older SOCs which use IBGR:555 (msb is intensity) rather
than BGR:565.

Use SOC-type to determine the pixel layout.

Tested on at91sam9263 and at91sam9g45.

Cc: <stable@vger.kernel.org>
Acked-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/video/atmel_lcdfb.c | 22 +++++++++++++++-------
 include/video/atmel_lcdc.h  |  1 +
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 12cf5f3..025428e 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -422,17 +422,22 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
 			= var->bits_per_pixel;
 		break;
 	case 16:
+		/* Older SOCs use IBGR:555 rather than BGR:565. */
+		if (sinfo->have_intensity_bit)
+			var->green.length = 5;
+		else
+			var->green.length = 6;
+
 		if (sinfo->lcd_wiring_mode = ATMEL_LCDC_WIRING_RGB) {
-			/* RGB:565 mode */
-			var->red.offset = 11;
+			/* RGB:5X5 mode */
+			var->red.offset = var->green.length + 5;
 			var->blue.offset = 0;
 		} else {
-			/* BGR:565 mode */
+			/* BGR:5X5 mode */
 			var->red.offset = 0;
-			var->blue.offset = 11;
+			var->blue.offset = var->green.length + 5;
 		}
 		var->green.offset = 5;
-		var->green.length = 6;
 		var->red.length = var->blue.length = 5;
 		break;
 	case 32:
@@ -679,8 +684,7 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red,
 
 	case FB_VISUAL_PSEUDOCOLOR:
 		if (regno < 256) {
-			if (cpu_is_at91sam9261() || cpu_is_at91sam9263()
-			    || cpu_is_at91sam9rl()) {
+			if (sinfo->have_intensity_bit) {
 				/* old style I+BGR:555 */
 				val  = ((red   >> 11) & 0x001f);
 				val |= ((green >>  6) & 0x03e0);
@@ -870,6 +874,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
 	}
 	sinfo->info = info;
 	sinfo->pdev = pdev;
+	if (cpu_is_at91sam9261() || cpu_is_at91sam9263() ||
+							cpu_is_at91sam9rl()) {
+		sinfo->have_intensity_bit = true;
+	}
 
 	strcpy(info->fix.id, sinfo->pdev->name);
 	info->flags = ATMEL_LCDFB_FBINFO_DEFAULT;
diff --git a/include/video/atmel_lcdc.h b/include/video/atmel_lcdc.h
index 28447f1..5f0e234 100644
--- a/include/video/atmel_lcdc.h
+++ b/include/video/atmel_lcdc.h
@@ -62,6 +62,7 @@ struct atmel_lcdfb_info {
 	void (*atmel_lcdfb_power_control)(int on);
 	struct fb_monspecs	*default_monspecs;
 	u32			pseudo_palette[16];
+	bool			have_intensity_bit;
 };
 
 #define ATMEL_LCDC_DMABADDR1	0x00
-- 
1.8.0


^ permalink raw reply related

* [PATCH 2/5] ARM: at91/neocore926: fix LCD-wiring mode
From: Nicolas Ferre @ 2013-02-08 16:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1360340791.git.nicolas.ferre@atmel.com>

From: Johan Hovold <jhovold@gmail.com>

Fix regression introduced by commit 787f9fd23283 ("atmel_lcdfb: support
16bit BGR:565 mode, remove unsupported 15bit modes") which broke 16-bpp
modes for older SOCs which use IBGR:555 (msb is intensity) rather than
BGR:565.

The above commit also removed the RGB:555-wiring hack without fixing the
neocore926 board which used it. Fix by specifying RGB-wiring and let the
driver handle the final SOC-dependant layout.

Remove the no longer used ATMEL_LCDC_WIRING_RGB555 define.

Compile-only tested.

Cc: <stable@vger.kernel.org>
Acked-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/board-neocore926.c | 2 +-
 include/video/atmel_lcdc.h            | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/arch/arm/mach-at91/board-neocore926.c b/arch/arm/mach-at91/board-neocore926.c
index bc7a1c4..4726297 100644
--- a/arch/arm/mach-at91/board-neocore926.c
+++ b/arch/arm/mach-at91/board-neocore926.c
@@ -266,7 +266,7 @@ static struct atmel_lcdfb_info __initdata neocore926_lcdc_data = {
 	.default_monspecs		= &at91fb_default_monspecs,
 	.atmel_lcdfb_power_control	= at91_lcdc_power_control,
 	.guard_time			= 1,
-	.lcd_wiring_mode		= ATMEL_LCDC_WIRING_RGB555,
+	.lcd_wiring_mode		= ATMEL_LCDC_WIRING_RGB,
 };
 
 #else
diff --git a/include/video/atmel_lcdc.h b/include/video/atmel_lcdc.h
index 5f0e234..8deb226 100644
--- a/include/video/atmel_lcdc.h
+++ b/include/video/atmel_lcdc.h
@@ -30,7 +30,6 @@
  */
 #define ATMEL_LCDC_WIRING_BGR	0
 #define ATMEL_LCDC_WIRING_RGB	1
-#define ATMEL_LCDC_WIRING_RGB555	2
 
 
  /* LCD Controller info data structure, stored in device platform_data */
-- 
1.8.0


^ permalink raw reply related

* [PATCH 3/5] ARM: at91/avr32/atmel_lcdfb: add bus-clock entry
From: Nicolas Ferre @ 2013-02-08 16:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1360340791.git.nicolas.ferre@atmel.com>

From: Johan Hovold <jhovold@gmail.com>

Add hclk entry for the atmel_lcdfb bus clock.

On at91sam9261, at91sam9g10 and at32ap the bus clock has to be enabled
as well as the peripheral clock. Add the appropriate lookup entries to
these SOCs and fake clocks to the SOCs that do not use it.

This allows us to get rid of the conditional enabling of the clocks in
the driver which relied on the cpu_is macros.

Tested on at91sam9263 and at91sam9g45, compile-tested for other
AT91-SOCs, and untested for AVR32.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/at91sam9261.c    |  1 +
 arch/arm/mach-at91/at91sam9263.c    |  1 +
 arch/arm/mach-at91/at91sam9g45.c    |  1 +
 arch/arm/mach-at91/at91sam9rl.c     |  1 +
 arch/avr32/mach-at32ap/at32ap700x.c |  4 ++--
 drivers/video/atmel_lcdfb.c         | 23 ++++++++---------------
 6 files changed, 14 insertions(+), 17 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 2998a08..5838f12 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -169,6 +169,7 @@ static struct clk *periph_clocks[] __initdata = {
 };
 
 static struct clk_lookup periph_clocks_lookups[] = {
+	CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &hck1),
 	CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
 	CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
 	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index b9fc60d..520a63d 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -190,6 +190,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
 	CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk),
 	CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk),
+	CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
 	CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
 	CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
 	CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index d3addee..ea6b62b 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -228,6 +228,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_ID("hclk", &macb_clk),
 	/* One additional fake clock for ohci */
 	CLKDEV_CON_ID("ohci_clk", &uhphs_clk),
+	CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
 	CLKDEV_CON_DEV_ID("ehci_clk", "atmel-ehci", &uhphs_clk),
 	CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk),
 	CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk),
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
index eb98704..4cd4fa9 100644
--- a/arch/arm/mach-at91/at91sam9rl.c
+++ b/arch/arm/mach-at91/at91sam9rl.c
@@ -179,6 +179,7 @@ static struct clk *periph_clocks[] __initdata = {
 };
 
 static struct clk_lookup periph_clocks_lookups[] = {
+	CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
 	CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk),
 	CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk),
 	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index b323d8d..cd25b01 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -1453,7 +1453,7 @@ static struct resource atmel_lcdfb0_resource[] = {
 	},
 };
 DEFINE_DEV_DATA(atmel_lcdfb, 0);
-DEV_CLK(hck1, atmel_lcdfb0, hsb, 7);
+DEV_CLK(hclk, atmel_lcdfb0, hsb, 7);
 static struct clk atmel_lcdfb0_pixclk = {
 	.name		= "lcdc_clk",
 	.dev		= &atmel_lcdfb0_device.dev,
@@ -2246,7 +2246,7 @@ static __initdata struct clk *init_clocks[] = {
 	&atmel_twi0_pclk,
 	&atmel_mci0_pclk,
 #if defined(CONFIG_CPU_AT32AP7000) || defined(CONFIG_CPU_AT32AP7002)
-	&atmel_lcdfb0_hck1,
+	&atmel_lcdfb0_hclk,
 	&atmel_lcdfb0_pixclk,
 #endif
 	&ssc0_pclk,
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 025428e..c5883ca 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -821,15 +821,13 @@ static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo)
 
 static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo)
 {
-	if (sinfo->bus_clk)
-		clk_enable(sinfo->bus_clk);
+	clk_enable(sinfo->bus_clk);
 	clk_enable(sinfo->lcdc_clk);
 }
 
 static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo)
 {
-	if (sinfo->bus_clk)
-		clk_disable(sinfo->bus_clk);
+	clk_disable(sinfo->bus_clk);
 	clk_disable(sinfo->lcdc_clk);
 }
 
@@ -888,13 +886,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
 	info->fix = atmel_lcdfb_fix;
 
 	/* Enable LCDC Clocks */
-	if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()
-	 || cpu_is_at32ap7000()) {
-		sinfo->bus_clk = clk_get(dev, "hck1");
-		if (IS_ERR(sinfo->bus_clk)) {
-			ret = PTR_ERR(sinfo->bus_clk);
-			goto free_info;
-		}
+	sinfo->bus_clk = clk_get(dev, "hclk");
+	if (IS_ERR(sinfo->bus_clk)) {
+		ret = PTR_ERR(sinfo->bus_clk);
+		goto free_info;
 	}
 	sinfo->lcdc_clk = clk_get(dev, "lcdc_clk");
 	if (IS_ERR(sinfo->lcdc_clk)) {
@@ -1055,8 +1050,7 @@ stop_clk:
 	atmel_lcdfb_stop_clock(sinfo);
 	clk_put(sinfo->lcdc_clk);
 put_bus_clk:
-	if (sinfo->bus_clk)
-		clk_put(sinfo->bus_clk);
+	clk_put(sinfo->bus_clk);
 free_info:
 	framebuffer_release(info);
 out:
@@ -1081,8 +1075,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev)
 	unregister_framebuffer(info);
 	atmel_lcdfb_stop_clock(sinfo);
 	clk_put(sinfo->lcdc_clk);
-	if (sinfo->bus_clk)
-		clk_put(sinfo->bus_clk);
+	clk_put(sinfo->bus_clk);
 	fb_dealloc_cmap(&info->cmap);
 	free_irq(sinfo->irq_base, info);
 	iounmap(sinfo->mmio);
-- 
1.8.0


^ permalink raw reply related

* [PATCH 4/5] atmel_lcdfb: move lcdcon2 register access to compute_hozval
From: Nicolas Ferre @ 2013-02-08 16:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1360340791.git.nicolas.ferre@atmel.com>

From: Johan Hovold <jhovold@gmail.com>

Pass atmel_lcd_info structure to compute_hozval and only do the register
access on SOCs that actually use it.

This will also simplify the removal of the cpu_is macros.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/video/atmel_lcdfb.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index c5883ca..2effd35 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -193,14 +193,17 @@ static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = {
 	.accel		= FB_ACCEL_NONE,
 };
 
-static unsigned long compute_hozval(unsigned long xres, unsigned long lcdcon2)
+static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo,
+							unsigned long xres)
 {
+	unsigned long lcdcon2;
 	unsigned long value;
 
 	if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10()
 		|| cpu_is_at32ap7000()))
 		return xres;
 
+	lcdcon2 = lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2);
 	value = xres;
 	if ((lcdcon2 & ATMEL_LCDC_DISTYPE) != ATMEL_LCDC_DISTYPE_TFT) {
 		/* STN display */
@@ -591,8 +594,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
 	lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value);
 
 	/* Horizontal value (aka line size) */
-	hozval_linesz = compute_hozval(info->var.xres,
-					lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2));
+	hozval_linesz = compute_hozval(sinfo, info->var.xres);
 
 	/* Display size */
 	value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
-- 
1.8.0


^ permalink raw reply related

* [PATCH 5/5] ARM: at91/avr32/atmel_lcdfb: add platform device-id table
From: Nicolas Ferre @ 2013-02-08 16:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1360340791.git.nicolas.ferre@atmel.com>

From: Johan Hovold <jhovold@gmail.com>

Add platform device-id table in order to identify the controller and
determine its configuration.

The currently used configuration parameters are:

have_alt_pixclock
 - SOC uses an alternate pixel-clock calculation formula (at91sam9g45
   non-ES)

have_hozval
 - SOC has a HOZVAL field in LCDFRMCFG which is used to determine the
   linesize for STN displays (at91sam9261, at921sam9g10 and at32ap)

have_intensity_bit
 - SOC uses IBGR:555 rather than BGR:565 16-bit pixel layout
   (at91sam9261, at91sam9263 and at91sam9rl)

This allows us to remove all the remaining uses of cpu_is macros from
the driver.

Tested on at91sam9263 and at91sam9g45, compile-tested for other
AT91-SOCs, and untested for AVR32.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/at91sam9261.c         |  3 +-
 arch/arm/mach-at91/at91sam9261_devices.c |  6 ++-
 arch/arm/mach-at91/at91sam9263.c         |  2 +-
 arch/arm/mach-at91/at91sam9263_devices.c |  2 +-
 arch/arm/mach-at91/at91sam9g45.c         |  3 +-
 arch/arm/mach-at91/at91sam9g45_devices.c |  6 ++-
 arch/arm/mach-at91/at91sam9rl.c          |  2 +-
 arch/arm/mach-at91/at91sam9rl_devices.c  |  2 +-
 arch/avr32/mach-at32ap/at32ap700x.c      |  2 +
 drivers/video/atmel_lcdfb.c              | 89 ++++++++++++++++++++++++++++----
 include/video/atmel_lcdc.h               |  4 +-
 11 files changed, 102 insertions(+), 19 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 5838f12..0204f4c 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -169,7 +169,8 @@ static struct clk *periph_clocks[] __initdata = {
 };
 
 static struct clk_lookup periph_clocks_lookups[] = {
-	CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &hck1),
+	CLKDEV_CON_DEV_ID("hclk", "at91sam9261-lcdfb.0", &hck1),
+	CLKDEV_CON_DEV_ID("hclk", "at91sam9g10-lcdfb.0", &hck1),
 	CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
 	CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
 	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index 92e0f86..629ea5f 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -488,7 +488,6 @@ static struct resource lcdc_resources[] = {
 };
 
 static struct platform_device at91_lcdc_device = {
-	.name		= "atmel_lcdfb",
 	.id		= 0,
 	.dev		= {
 				.dma_mask		= &lcdc_dmamask,
@@ -505,6 +504,11 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data)
 		return;
 	}
 
+	if (cpu_is_at91sam9g10())
+		at91_lcdc_device.name = "at91sam9g10-lcdfb";
+	else
+		at91_lcdc_device.name = "at91sam9261-lcdfb";
+
 #if defined(CONFIG_FB_ATMEL_STN)
 	at91_set_A_periph(AT91_PIN_PB0, 0);     /* LCDVSYNC */
 	at91_set_A_periph(AT91_PIN_PB1, 0);     /* LCDHSYNC */
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index 520a63d..2282fd7 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -190,7 +190,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
 	CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk),
 	CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk),
-	CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
+	CLKDEV_CON_DEV_ID("hclk", "at91sam9263-lcdfb.0", &lcdc_clk),
 	CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
 	CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
 	CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index ed666f5..858c8aa 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -848,7 +848,7 @@ static struct resource lcdc_resources[] = {
 };
 
 static struct platform_device at91_lcdc_device = {
-	.name		= "atmel_lcdfb",
+	.name		= "at91sam9263-lcdfb",
 	.id		= 0,
 	.dev		= {
 				.dma_mask		= &lcdc_dmamask,
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index ea6b62b..c68960d 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -228,7 +228,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_ID("hclk", &macb_clk),
 	/* One additional fake clock for ohci */
 	CLKDEV_CON_ID("ohci_clk", &uhphs_clk),
-	CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
+	CLKDEV_CON_DEV_ID("hclk", "at91sam9g45-lcdfb.0", &lcdc_clk),
+	CLKDEV_CON_DEV_ID("hclk", "at91sam9g45es-lcdfb.0", &lcdc_clk),
 	CLKDEV_CON_DEV_ID("ehci_clk", "atmel-ehci", &uhphs_clk),
 	CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk),
 	CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk),
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index 827c9f2..fe626d4 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -981,7 +981,6 @@ static struct resource lcdc_resources[] = {
 };
 
 static struct platform_device at91_lcdc_device = {
-	.name		= "atmel_lcdfb",
 	.id		= 0,
 	.dev		= {
 				.dma_mask		= &lcdc_dmamask,
@@ -997,6 +996,11 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data)
 	if (!data)
 		return;
 
+	if (cpu_is_at91sam9g45es())
+		at91_lcdc_device.name = "at91sam9g45es-lcdfb";
+	else
+		at91_lcdc_device.name = "at91sam9g45-lcdfb";
+
 	at91_set_A_periph(AT91_PIN_PE0, 0);	/* LCDDPWR */
 
 	at91_set_A_periph(AT91_PIN_PE2, 0);	/* LCDCC */
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
index 4cd4fa9..3de3e04 100644
--- a/arch/arm/mach-at91/at91sam9rl.c
+++ b/arch/arm/mach-at91/at91sam9rl.c
@@ -179,7 +179,7 @@ static struct clk *periph_clocks[] __initdata = {
 };
 
 static struct clk_lookup periph_clocks_lookups[] = {
-	CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
+	CLKDEV_CON_DEV_ID("hclk", "at91sam9rl-lcdfb.0", &lcdc_clk),
 	CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk),
 	CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk),
 	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
index ddf223f..352468f 100644
--- a/arch/arm/mach-at91/at91sam9rl_devices.c
+++ b/arch/arm/mach-at91/at91sam9rl_devices.c
@@ -514,7 +514,7 @@ static struct resource lcdc_resources[] = {
 };
 
 static struct platform_device at91_lcdc_device = {
-	.name		= "atmel_lcdfb",
+	.name		= "at91sam9rl-lcdfb",
 	.id		= 0,
 	.dev		= {
 				.dma_mask		= &lcdc_dmamask,
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index cd25b01..7c2f668 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -1530,6 +1530,8 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
 	memcpy(info, data, sizeof(struct atmel_lcdfb_info));
 	info->default_monspecs = monspecs;
 
+	pdev->name = "at32ap-lcdfb";
+
 	platform_device_register(pdev);
 	return pdev;
 
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 2effd35..c1a2914 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -34,6 +34,77 @@
 #define ATMEL_LCDC_DMA_BURST_LEN	8	/* words */
 #define ATMEL_LCDC_FIFO_SIZE		512	/* words */
 
+struct atmel_lcdfb_config {
+	bool have_alt_pixclock;
+	bool have_hozval;
+	bool have_intensity_bit;
+};
+
+static struct atmel_lcdfb_config at91sam9261_config = {
+	.have_hozval		= true,
+	.have_intensity_bit	= true,
+};
+
+static struct atmel_lcdfb_config at91sam9263_config = {
+	.have_intensity_bit	= true,
+};
+
+static struct atmel_lcdfb_config at91sam9g10_config = {
+	.have_hozval		= true,
+};
+
+static struct atmel_lcdfb_config at91sam9g45_config = {
+	.have_alt_pixclock	= true,
+};
+
+static struct atmel_lcdfb_config at91sam9g45es_config = {
+};
+
+static struct atmel_lcdfb_config at91sam9rl_config = {
+	.have_intensity_bit	= true,
+};
+
+static struct atmel_lcdfb_config at32ap_config = {
+	.have_hozval		= true,
+};
+
+static const struct platform_device_id atmel_lcdfb_devtypes[] = {
+	{
+		.name = "at91sam9261-lcdfb",
+		.driver_data = (unsigned long)&at91sam9261_config,
+	}, {
+		.name = "at91sam9263-lcdfb",
+		.driver_data = (unsigned long)&at91sam9263_config,
+	}, {
+		.name = "at91sam9g10-lcdfb",
+		.driver_data = (unsigned long)&at91sam9g10_config,
+	}, {
+		.name = "at91sam9g45-lcdfb",
+		.driver_data = (unsigned long)&at91sam9g45_config,
+	}, {
+		.name = "at91sam9g45es-lcdfb",
+		.driver_data = (unsigned long)&at91sam9g45es_config,
+	}, {
+		.name = "at91sam9rl-lcdfb",
+		.driver_data = (unsigned long)&at91sam9rl_config,
+	}, {
+		.name = "at32ap-lcdfb",
+		.driver_data = (unsigned long)&at32ap_config,
+	}, {
+		/* terminator */
+	}
+};
+
+static struct atmel_lcdfb_config *
+atmel_lcdfb_get_config(struct platform_device *pdev)
+{
+	unsigned long data;
+
+	data = platform_get_device_id(pdev)->driver_data;
+
+	return (struct atmel_lcdfb_config *)data;
+}
+
 #if defined(CONFIG_ARCH_AT91)
 #define	ATMEL_LCDFB_FBINFO_DEFAULT	(FBINFO_DEFAULT \
 					 | FBINFO_PARTIAL_PAN_OK \
@@ -199,8 +270,7 @@ static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo,
 	unsigned long lcdcon2;
 	unsigned long value;
 
-	if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10()
-		|| cpu_is_at32ap7000()))
+	if (!sinfo->config->have_hozval)
 		return xres;
 
 	lcdcon2 = lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2);
@@ -426,7 +496,7 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
 		break;
 	case 16:
 		/* Older SOCs use IBGR:555 rather than BGR:565. */
-		if (sinfo->have_intensity_bit)
+		if (sinfo->config->have_intensity_bit)
 			var->green.length = 5;
 		else
 			var->green.length = 6;
@@ -534,7 +604,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
 	/* Now, the LCDC core... */
 
 	/* Set pixel clock */
-	if (cpu_is_at91sam9g45() && !cpu_is_at91sam9g45es())
+	if (sinfo->config->have_alt_pixclock)
 		pix_factor = 1;
 
 	clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;
@@ -686,7 +756,7 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red,
 
 	case FB_VISUAL_PSEUDOCOLOR:
 		if (regno < 256) {
-			if (sinfo->have_intensity_bit) {
+			if (sinfo->config->have_intensity_bit) {
 				/* old style I+BGR:555 */
 				val  = ((red   >> 11) & 0x001f);
 				val |= ((green >>  6) & 0x03e0);
@@ -874,10 +944,9 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
 	}
 	sinfo->info = info;
 	sinfo->pdev = pdev;
-	if (cpu_is_at91sam9261() || cpu_is_at91sam9263() ||
-							cpu_is_at91sam9rl()) {
-		sinfo->have_intensity_bit = true;
-	}
+	sinfo->config = atmel_lcdfb_get_config(pdev);
+	if (!sinfo->config)
+		goto free_info;
 
 	strcpy(info->fix.id, sinfo->pdev->name);
 	info->flags = ATMEL_LCDFB_FBINFO_DEFAULT;
@@ -1146,7 +1215,7 @@ static struct platform_driver atmel_lcdfb_driver = {
 	.remove		= __exit_p(atmel_lcdfb_remove),
 	.suspend	= atmel_lcdfb_suspend,
 	.resume		= atmel_lcdfb_resume,
-
+	.id_table	= atmel_lcdfb_devtypes,
 	.driver		= {
 		.name	= "atmel_lcdfb",
 		.owner	= THIS_MODULE,
diff --git a/include/video/atmel_lcdc.h b/include/video/atmel_lcdc.h
index 8deb226..0f5a2fc 100644
--- a/include/video/atmel_lcdc.h
+++ b/include/video/atmel_lcdc.h
@@ -31,6 +31,7 @@
 #define ATMEL_LCDC_WIRING_BGR	0
 #define ATMEL_LCDC_WIRING_RGB	1
 
+struct atmel_lcdfb_config;
 
  /* LCD Controller info data structure, stored in device platform_data */
 struct atmel_lcdfb_info {
@@ -61,7 +62,8 @@ struct atmel_lcdfb_info {
 	void (*atmel_lcdfb_power_control)(int on);
 	struct fb_monspecs	*default_monspecs;
 	u32			pseudo_palette[16];
-	bool			have_intensity_bit;
+
+	struct atmel_lcdfb_config *config;
 };
 
 #define ATMEL_LCDC_DMABADDR1	0x00
-- 
1.8.0


^ permalink raw reply related

* Re: [PATCH 0/5] at91: atmel_lcdfb: regression fixes and cpu_is removal
From: Jean-Christophe PLAGNIOL-VILLARD @ 2013-02-08 16:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1360340791.git.nicolas.ferre@atmel.com>

HI,

on all

Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

Best Regards,
J.
On 17:35 Fri 08 Feb     , Nicolas Ferre wrote:
> These patches fix a regression in 16-bpp support for older SOCs which use
> IBGR:555 rather than BGR:565 pixel layout. Use SOC-type to determine if the
> controller uses the intensity-bit and restore the old layout in that case.
> 
> The last patch is a removal of uses of cpu_is_xxxx() macros in atmel_lcdfb with
> a platform-device-id table and static configurations.
> 
> 
> Patches from Johan Hovold taken from:
> "[PATCH 0/3] atmel_lcdfb: fix 16-bpp regression"
> and
> "[PATCH v2 0/3] ARM: at91/avr32/atmel_lcdfb: remove cpu_is macros"
> patch series to form a clean patch series with my signature.
> 
> Arnd, Olof,
> as it seems that old fbdev drivers are not so much reviewed those days, can we
> take the decision to queue this material through arm-soc with other AT91
> drivers updates?
> 
> Best regards,
> 
> Johan Hovold (5):
>   atmel_lcdfb: fix 16-bpp modes on older SOCs
>   ARM: at91/neocore926: fix LCD-wiring mode
>   ARM: at91/avr32/atmel_lcdfb: add bus-clock entry
>   atmel_lcdfb: move lcdcon2 register access to compute_hozval
>   ARM: at91/avr32/atmel_lcdfb: add platform device-id table
> 
>  arch/arm/mach-at91/at91sam9261.c         |   2 +
>  arch/arm/mach-at91/at91sam9261_devices.c |   6 +-
>  arch/arm/mach-at91/at91sam9263.c         |   1 +
>  arch/arm/mach-at91/at91sam9263_devices.c |   2 +-
>  arch/arm/mach-at91/at91sam9g45.c         |   2 +
>  arch/arm/mach-at91/at91sam9g45_devices.c |   6 +-
>  arch/arm/mach-at91/at91sam9rl.c          |   1 +
>  arch/arm/mach-at91/at91sam9rl_devices.c  |   2 +-
>  arch/arm/mach-at91/board-neocore926.c    |   2 +-
>  arch/avr32/mach-at32ap/at32ap700x.c      |   6 +-
>  drivers/video/atmel_lcdfb.c              | 130 ++++++++++++++++++++++++-------
>  include/video/atmel_lcdc.h               |   4 +-
>  12 files changed, 127 insertions(+), 37 deletions(-)
> 
> -- 
> 1.8.0
> 

^ permalink raw reply

* Re: [PATCH 0/5] at91: atmel_lcdfb: regression fixes and cpu_is removal
From: Olof Johansson @ 2013-02-10  0:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1360340791.git.nicolas.ferre@atmel.com>

On Fri, Feb 08, 2013 at 05:35:13PM +0100, Nicolas Ferre wrote:
> These patches fix a regression in 16-bpp support for older SOCs which use
> IBGR:555 rather than BGR:565 pixel layout. Use SOC-type to determine if the
> controller uses the intensity-bit and restore the old layout in that case.
> 
> The last patch is a removal of uses of cpu_is_xxxx() macros in atmel_lcdfb with
> a platform-device-id table and static configurations.
> 
> 
> Patches from Johan Hovold taken from:
> "[PATCH 0/3] atmel_lcdfb: fix 16-bpp regression"
> and
> "[PATCH v2 0/3] ARM: at91/avr32/atmel_lcdfb: remove cpu_is macros"
> patch series to form a clean patch series with my signature.
> 
> Arnd, Olof,
> as it seems that old fbdev drivers are not so much reviewed those days, can we
> take the decision to queue this material through arm-soc with other AT91
> drivers updates?

It would be beneficial to get an ack from Florian. Was he involved in the
review of the code that regressed 16-bpp support in the first place? When was
the regression introduced?


-Olof

^ permalink raw reply

* Re: [PATCH 0/5] at91: atmel_lcdfb: regression fixes and cpu_is removal
From: Johan Hovold @ 2013-02-10 18:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130210004740.GH16278@quad.lixom.net>

On Sun, Feb 10, 2013 at 1:47 AM, Olof Johansson <olof@lixom.net> wrote:
> On Fri, Feb 08, 2013 at 05:35:13PM +0100, Nicolas Ferre wrote:
>> These patches fix a regression in 16-bpp support for older SOCs which
>> use IBGR:555 rather than BGR:565 pixel layout. Use SOC-type to
>> determine if the controller uses the intensity-bit and restore the
>> old layout in that case.
>>
>> The last patch is a removal of uses of cpu_is_xxxx() macros in
>> atmel_lcdfb with a platform-device-id table and static
>> configurations.
>>
>>
>> Patches from Johan Hovold taken from: "[PATCH 0/3] atmel_lcdfb: fix
>> 16-bpp regression" and "[PATCH v2 0/3] ARM: at91/avr32/atmel_lcdfb:
>> remove cpu_is macros" patch series to form a clean patch series with
>> my signature.
>>
>> Arnd, Olof, as it seems that old fbdev drivers are not so much
>> reviewed those days, can we take the decision to queue this material
>> through arm-soc with other AT91 drivers updates?
>
> It would be beneficial to get an ack from Florian. Was he involved in
> the review of the code that regressed 16-bpp support in the first
> place? When was the regression introduced?

In v3.4 by commit 787f9fd2328 ("atmel_lcdfb: support 16bit BGR:565 mode,
remove unsupported 15bit modes").

Johan

^ permalink raw reply

* [PATCH] video/modedb: fb_find_nearest_mode: take vmode into account
From: Floris Bos @ 2013-02-10 20:23 UTC (permalink / raw)
  To: linux-fbdev

Previously fb_find_nearest_mode() only searched the modelist for a video mode that best matches the
desired resolution and refresh rate.
With this patch it also takes the vmode into account if there is more then one mode with the same
resolution and refresh rate.
Typical use case is HDMI TVs that support both 1080p60 and 1080i60

Signed-off-by: Floris Bos <bos@je-eigen-domein.nl>
---
 drivers/video/modedb.c |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index a9a907c..e852371 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -913,6 +913,8 @@ const struct fb_videomode *fb_find_best_mode(const struct fb_var_screeninfo *var
  * Finds best matching videomode, smaller or greater in dimension.
  * If more than 1 videomode is found, will return the videomode with
  * the closest refresh rate.
+ * If multiple modes with the same resolution and refresh rate are found
+ * pick the one with the matching vmode (e.g. non-interlaced)
  */
 const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode,
 					        struct list_head *head)
@@ -939,6 +941,8 @@ const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode,
 			if (diff_refresh > d) {
 				diff_refresh = d;
 				best = cmode;
+			} else if (diff_refresh = d && cmode->vmode = mode->vmode) {
+				best = cmode;
 			}
 		}
 	}
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH v2 1/1] OMAP4: DSS: Add panel for Blaze Tablet boards
From: Andi Shyti @ 2013-02-10 23:51 UTC (permalink / raw)
  To: Ruslan Bilovol
  Cc: andi, tomi.valkeinen, FlorianSchandinat, linux-fbdev,
	linux-kernel, linux-omap
In-Reply-To: <1360338220-12753-2-git-send-email-ruslan.bilovol@ti.com>

Hi Ruslan,

> TC358765 is DSI-to-LVDS transmitter from Toshiba, used in
> OMAP44XX Blaze Tablet and Blaze Tablet2 boards.

I think it's fine, just some nitpicks and checkpatch warnings

> +struct {
> +	struct device *dev;
> +	struct dentry *dir;
> +} tc358765_debug;

Should this be static?

> +struct tc358765_reg {
> +	const char *name;
> +	u16 reg;
> +	u8 perm:2;
> +} tc358765_regs[] = {

Should this be static as well?

> +	{ "D1S_ZERO", D1S_ZERO, A_RW },
> +	{ "D2S_ZERO", D2S_ZERO, A_RW  },
> +	{ "D3S_ZERO", D3S_ZERO, A_RW },
> +	{ "PPI_CLRFLG", PPI_CLRFLG, A_RW },
> +	{ "PPI_CLRSIPO", PPI_CLRSIPO, A_RW },
> +	{ "PPI_HSTimeout", PPI_HSTimeout, A_RW },

WARNING: Avoid CamelCase: <PPI_HSTimeout>
#136: FILE: video/omap2/displays/panel-tc358765.c:136:
+	{ "PPI_HSTimeout", PPI_HSTimeout, A_RW },

> +	{ "PPI_HSTimeoutEnable", PPI_HSTimeoutEnable, A_RW },

WARNING: Avoid CamelCase: <PPI_HSTimeoutEnable>
#137: FILE: video/omap2/displays/panel-tc358765.c:137:
+	{ "PPI_HSTimeoutEnable", PPI_HSTimeoutEnable, A_RW },


> +static int tc358765_read_block(u16 reg, u8 *data, int len)
> +{
> +	unsigned char wb[2];
> +	struct i2c_msg msg[2];
> +	int r;
> +	mutex_lock(&tc358765_i2c->xfer_lock);
> +	wb[0] = (reg & 0xff00) >> 8;
> +	wb[1] = reg & 0xff;
> +	msg[0].addr = tc358765_i2c->client->addr;
> +	msg[0].len = 2;
> +	msg[0].flags = 0;
> +	msg[0].buf = wb;
> +	msg[1].addr = tc358765_i2c->client->addr;
> +	msg[1].flags = I2C_M_RD;
> +	msg[1].len = len;
> +	msg[1].buf = data;
> +
> +	r = i2c_transfer(tc358765_i2c->client->adapter, msg, ARRAY_SIZE(msg));
> +	mutex_unlock(&tc358765_i2c->xfer_lock);
> +
> +	if (r = ARRAY_SIZE(msg))
> +		return len;
> +
> +	return r;

If you like, here you could do

	return (r = ARRAY_SIZE(msg)) ? len : r;

Usually I like more this notation because keeps the code more
compact and immediate to read, but you don't have to

> +	mutex_lock(&tc358765_i2c->xfer_lock);
> +	ret = i2c_transfer(tc358765_i2c->client->adapter, &msg, 1);
> +	mutex_unlock(&tc358765_i2c->xfer_lock);
> +
> +	if (ret != 1)
> +		return ret;
> +	return 0;

Also here you could do

	return (ret != 1) ? ret : 0;

But this is more taste :)

> +static ssize_t tc358765_seq_write(struct file *filp, const char __user *ubuf,
> +						size_t size, loff_t *ppos)
> +{
> +	struct device *dev = tc358765_debug.dev;
> +	unsigned i, reg_count;
> +	u32 value = 0;
> +	int error = 0;
> +	/* kids, don't use register names that long */

I won't, promised, but please, drop this comment :)

> +	char name[30];
> +	char buf[50];
> +
> +	if (size >= sizeof(buf))
> +		size = sizeof(buf);

what's the point of this?

> +static void tc358765_uninitialize_debugfs(void)
> +{
> +	if (tc358765_debug.dir)
> +		debugfs_remove_recursive(tc358765_debug.dir);

WARNING: debugfs_remove_recursive(NULL) is safe this check is probably not required
#435: FILE: video/omap2/displays/panel-tc358765.c:435:
+	if (tc358765_debug.dir)
+		debugfs_remove_recursive(tc358765_debug.dir);


> +static struct tc358765_board_data *get_board_data(struct omap_dss_device
> +								*dssdev)
> +{
> +	return (struct tc358765_board_data *)dssdev->data;

You shouldn't need for this cast, does it complain?

> +}
> +
> +static void tc358765_get_timings(struct omap_dss_device *dssdev,
> +		struct omap_video_timings *timings)
> +{
> +	*timings = dssdev->panel.timings;
> +}
> +
> +static void tc358765_set_timings(struct omap_dss_device *dssdev,
> +		struct omap_video_timings *timings)
> +{
> +	dev_info(&dssdev->dev, "set_timings() not implemented\n");

... then drop the function :)

> +	if ((pins[2] & 1) || (pins[3] & 1)) {
> +		lanes |= (1 << 1);
> +		ret |= tc358765_write_register(dssdev, PPI_D0S_CLRSIPOCOUNT,
> +							board_data->clrsipo);
> +	}
> +	if ((pins[4] & 1) || (pins[5] & 1)) {
> +		lanes |= (1 << 2);
> +		ret |= tc358765_write_register(dssdev, PPI_D1S_CLRSIPOCOUNT,
> +							board_data->clrsipo);
> +	}
> +	if ((pins[6] & 1) || (pins[7] & 1)) {
> +		lanes |= (1 << 3);
> +		ret |= tc358765_write_register(dssdev, PPI_D2S_CLRSIPOCOUNT,
> +							board_data->clrsipo);
> +	}
> +	if ((pins[8] & 1) || (pins[9] & 1)) {
> +		lanes |= (1 << 4);
> +		ret |= tc358765_write_register(dssdev, PPI_D3S_CLRSIPOCOUNT,
> +							board_data->clrsipo);
> +	}

Can't this be done in one single multiwrighting command since
this registers are consecutive?

You build once the array to write and you send it at once.

Moreover it would be nice to have a name for all those nombers

> +	ret |= tc358765_write_register(dssdev, HTIM1,
> +		(tc358765_timings.hbp << 16) | tc358765_timings.hsw);
> +	ret |= tc358765_write_register(dssdev, HTIM2,
> +		((tc358765_timings.hfp << 16) | tc358765_timings.x_res));
> +	ret |= tc358765_write_register(dssdev, VTIM1,
> +		((tc358765_timings.vbp << 16) |	tc358765_timings.vsw));
> +	ret |= tc358765_write_register(dssdev, VTIM2,
> +		((tc358765_timings.vfp << 16) |	tc358765_timings.y_res));

also this and all the other cases I haven't checked

> +static int tc358765_enable(struct omap_dss_device *dssdev)
> +{
> +	struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
> +	int r = 0;

this initialisation is useless

> +	if (r) {
> +		dev_dbg(&dssdev->dev, "enable failed\n");

Here you could choose a different print level, I would have used
dev_err instead.

> +static int tc358765_i2c_probe(struct i2c_client *client,
> +				   const struct i2c_device_id *id)
> +{
> +	tc358765_i2c = devm_kzalloc(&client->dev, sizeof(*tc358765_i2c), GFP_KERNEL);

WARNING: line over 80 characters
#927: FILE: video/omap2/displays/panel-tc358765.c:927:
+	tc358765_i2c = devm_kzalloc(&client->dev, sizeof(*tc358765_i2c), GFP_KERNEL);


> +	/* store i2c_client pointer on private data structure */
> +	tc358765_i2c->client = client;
> +
> +	/* store private data structure pointer on i2c_client structure */
> +	i2c_set_clientdata(client, tc358765_i2c);
> +
> +	/* init mutex */
> +	mutex_init(&tc358765_i2c->xfer_lock);
> +	dev_err(&client->dev, "D2L i2c initialized\n");

while here dev_dbg (or dev_info) are better.

> +static int __init tc358765_init(void)
> +{
> +	int r;
> +	tc358765_i2c = NULL;
> +	r = i2c_add_driver(&tc358765_i2c_driver);
> +	if (r < 0) {
> +		printk(KERN_WARNING "d2l i2c driver registration failed\n");

WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ...  then pr_warn(...  to printk(KERN_WARNING ...
#981: FILE: video/omap2/displays/panel-tc358765.c:981:
+		printk(KERN_WARNING "d2l i2c driver registration failed\n");


Andi

^ permalink raw reply

* Re: [RFC PATCH 0/4] Common Display Framework-TF
From: Tomi Valkeinen @ 2013-02-11  8:21 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa,
	dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org,
	linux-samsung-soc@vger.kernel.org, kyungmin.park@samsung.com,
	m.szyprowski@samsung.com, Daniel Vetter, Vikas Sajjan,
	inki.dae@samsung.com, dh09.lee@samsung.com,
	ville.syrjala@intel.com, s.nawrocki@samsung.com
In-Reply-To: <511511B3.3060404@stericsson.com>

[-- Attachment #1: Type: text/plain, Size: 4218 bytes --]

On 2013-02-08 16:54, Marcus Lorentzon wrote:
> On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
>> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>>
>>> When we do that we stop->setup->start during blanking. So our "DSS" is
>>> optimized to be able to do that without getting blocked. All DSI video
>>> mode panels (and DPI) products we have done so far have not had any
>>> issue with that (as long as DSI HS clock is set to continuous). I think
>>> this approach is less platform dependant, as long as there is no SoC
>>> that take more than a blanking period to reconfigure.
>> So do you stop, setup and start the link with CPU, and this has to be
>> happen during blanking? Isn't that prone to errors? Or did you mean that
>> the hardware handles that automatically?
>>
>> In OMAP DSS there are so called shadow registers, that can be programmed
>> at any time. The you set a bit (GO bit), which tells the hardware to
>> take the new settings into use at the next vblank.
>>
>>  From DSI driver's perspective the link is never stopped when
>> reconfiguring the video timings. However, many other settings have to be
>> configured when the link is disabled.
> 
> Yeah, you lucky guys with the GO bit ;). No, we actually do CPU
> stop,setup,start. But since it is video mode, master is driving the sync
> so it is not a hard deadline. It is enough to restart before pixels
> start to degrade. On an LCD that is not so much time, but on an OLED it
> could be 10 secs :). Anyway, we have had several mass products with this
> soft solution and it has worked well.

Ah, ok. But in that case what you said in an earlier mail is not quite
correct: "I think this approach is less platform dependant, as long as
there is no SoC that take more than a blanking period to reconfigure.".
So in your approach the reconfiguration doesn't have to be done inside
the blanking period, but before the panel's picture starts to fade?

I don't know... It's early Monday morning, and not enough coffee yet,
but I get a bit uneasy feeling if I think that your method of
reconfiguring would be the only think allowed by the CDF API.

Some SoCs do support reconfiguring on the fly, without disabling the
link. It would not be nice if we didn't allow this to happen. And
actually, we're not only talking about SoCs here, but also any display
devices, like external buffer chips etc. They may also offer means to
change configs on the fly.

Well, I don't have any hard point about this at the moment, but I think
we should think different approaches how the configuration can be done.

> I understand, but removing the omap prefix doesn't mean it has to go in
> the panel slave port/bus settings. I still can't see why this should be
> configuration on the panel driver and not the DSI master driver. Number
> of pins might be useful since you might start with one lane and then
> activate the rest. But partial muxing (pre pinmux) doesn't seem to be
> something the panel should control or know anything about. Sounds like
> normal platform/DT data per product/board.

I think one case where this kind of pin configuration is needed, and
which also dictates that all panel related configuration has to be in
the panel's data, not in the DSI master's data, is hotplug.

If you have a board that has two panels connected to the same video
output, probably via some kind of mux, at least for the sensitive pins
like DSI, only one of the panels can be enabled at a time. The panels
can have different wiring, and thus the panel driver would need to
configure everything related to the bus when it's starting up.

The same also happens if you have a true hotplug, i.e. you can remove
the panel totally and plug in a new one. Again the wiring can be
different, and needs to be set up.

And, as I said, this means that all relevant data about the video bus
has to be in the panel's data, so that each panel can have its own set
of, say, pin configuration.

Hotplug is not a use case we should aim to support for the CDF v1, but I
think we should strive not to prevent hotplug either. So if we can
design the API so that hotplug is possible, I say let's do that.

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply

* Re: [linux-sunxi] [PATCH] video/modedb: fb_find_nearest_mode: take vmode into account
From: Hans de Goede @ 2013-02-11  9:28 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1360527814-28883-1-git-send-email-bos@je-eigen-domein.nl>

Hi,

On 02/10/2013 09:23 PM, Floris Bos wrote:
> Previously fb_find_nearest_mode() only searched the modelist for a video mode that best matches the
> desired resolution and refresh rate.
> With this patch it also takes the vmode into account if there is more then one mode with the same
> resolution and refresh rate.
> Typical use case is HDMI TVs that support both 1080p60 and 1080i60
>
> Signed-off-by: Floris Bos <bos@je-eigen-domein.nl>
> ---
>   drivers/video/modedb.c |    4 ++++
>   1 file changed, 4 insertions(+)
>
> diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
> index a9a907c..e852371 100644
> --- a/drivers/video/modedb.c
> +++ b/drivers/video/modedb.c
> @@ -913,6 +913,8 @@ const struct fb_videomode *fb_find_best_mode(const struct fb_var_screeninfo *var
>    * Finds best matching videomode, smaller or greater in dimension.
>    * If more than 1 videomode is found, will return the videomode with
>    * the closest refresh rate.
> + * If multiple modes with the same resolution and refresh rate are found
> + * pick the one with the matching vmode (e.g. non-interlaced)
>    */
>   const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode,
>   					        struct list_head *head)
> @@ -939,6 +941,8 @@ const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode,
>   			if (diff_refresh > d) {
>   				diff_refresh = d;
>   				best = cmode;
> +			} else if (diff_refresh = d && cmode->vmode = mode->vmode) {
> +				best = cmode;
>   			}
>   		}
>   	}
>

Hmm, my version of this patch was more conservative, only comparing the INTERLACED bit
of vmode. I assume you've tested this, and it works as advertised? If so ack.

Regards,

Hans



^ permalink raw reply

* Re: [RFC PATCH 0/4] Common Display Framework-TF
From: Marcus Lorentzon @ 2013-02-11  9:31 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa,
	dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org,
	linux-samsung-soc@vger.kernel.org, kyungmin.park@samsung.com,
	m.szyprowski@samsung.com, Daniel Vetter, Vikas Sajjan,
	inki.dae@samsung.com, dh09.lee@samsung.com,
	ville.syrjala@intel.com, s.nawrocki@samsung.com
In-Reply-To: <5118AA00.2000505@ti.com>

On 02/11/2013 09:21 AM, Tomi Valkeinen wrote:
> On 2013-02-08 16:54, Marcus Lorentzon wrote:
>> On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
>>> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>>>
>>>> When we do that we stop->setup->start during blanking. So our "DSS" is
>>>> optimized to be able to do that without getting blocked. All DSI video
>>>> mode panels (and DPI) products we have done so far have not had any
>>>> issue with that (as long as DSI HS clock is set to continuous). I think
>>>> this approach is less platform dependant, as long as there is no SoC
>>>> that take more than a blanking period to reconfigure.
>>> So do you stop, setup and start the link with CPU, and this has to be
>>> happen during blanking? Isn't that prone to errors? Or did you mean that
>>> the hardware handles that automatically?
>>>
>>> In OMAP DSS there are so called shadow registers, that can be programmed
>>> at any time. The you set a bit (GO bit), which tells the hardware to
>>> take the new settings into use at the next vblank.
>>>
>>>   From DSI driver's perspective the link is never stopped when
>>> reconfiguring the video timings. However, many other settings have to be
>>> configured when the link is disabled.
>> Yeah, you lucky guys with the GO bit ;). No, we actually do CPU
>> stop,setup,start. But since it is video mode, master is driving the sync
>> so it is not a hard deadline. It is enough to restart before pixels
>> start to degrade. On an LCD that is not so much time, but on an OLED it
>> could be 10 secs :). Anyway, we have had several mass products with this
>> soft solution and it has worked well.
> Ah, ok. But in that case what you said in an earlier mail is not quite
> correct: "I think this approach is less platform dependant, as long as
> there is no SoC that take more than a blanking period to reconfigure.".
> So in your approach the reconfiguration doesn't have to be done inside
> the blanking period, but before the panel's picture starts to fade?
>
> I don't know... It's early Monday morning, and not enough coffee yet,
> but I get a bit uneasy feeling if I think that your method of
> reconfiguring would be the only think allowed by the CDF API.
>
> Some SoCs do support reconfiguring on the fly, without disabling the
> link. It would not be nice if we didn't allow this to happen. And
> actually, we're not only talking about SoCs here, but also any display
> devices, like external buffer chips etc. They may also offer means to
> change configs on the fly.
>
> Well, I don't have any hard point about this at the moment, but I think
> we should think different approaches how the configuration can be done.

Ok, so what about a compromise which I think would work for "both" HWs 
... we allow the "configure" operation during video on, then each HW 
driver could decide if this means you have to stop or not to do the 
changes required. But then it is also important that we keep all config 
in one struct and not split it up. Otherwise HW like ours that has so be 
stopped could need to stop once for each setting/operation called.
So in short, allow configure(bus_params) during video on, keep all 
bus_params in the struct. It is in kernel struct so it can easily be 
changed/refactored.
>
>> I understand, but removing the omap prefix doesn't mean it has to go in
>> the panel slave port/bus settings. I still can't see why this should be
>> configuration on the panel driver and not the DSI master driver. Number
>> of pins might be useful since you might start with one lane and then
>> activate the rest. But partial muxing (pre pinmux) doesn't seem to be
>> something the panel should control or know anything about. Sounds like
>> normal platform/DT data per product/board.
> I think one case where this kind of pin configuration is needed, and
> which also dictates that all panel related configuration has to be in
> the panel's data, not in the DSI master's data, is hotplug.
>
> If you have a board that has two panels connected to the same video
> output, probably via some kind of mux, at least for the sensitive pins
> like DSI, only one of the panels can be enabled at a time. The panels
> can have different wiring, and thus the panel driver would need to
> configure everything related to the bus when it's starting up.
>
> The same also happens if you have a true hotplug, i.e. you can remove
> the panel totally and plug in a new one. Again the wiring can be
> different, and needs to be set up.
>
> And, as I said, this means that all relevant data about the video bus
> has to be in the panel's data, so that each panel can have its own set
> of, say, pin configuration.
>
> Hotplug is not a use case we should aim to support for the CDF v1, but I
> think we should strive not to prevent hotplug either. So if we can
> design the API so that hotplug is possible, I say let's do that.
>
Again, this probing and bus muxing is platform/bus specific and not 
panel specific. Any panel of that type will only ever work on Omap (or 
someone else implementing the same muxing features) as I see it. So why 
not just put that config on the bus master, dispc? I still can't see how 
this is panel config. You are only pushing CDF API and meta data to 
describe something that is only needed by one bus master. I have never 
seen any DSI slave that can change their pin config. And since there is 
no generic hot plug detect of DSI panels, at least not before DSI bus is 
available, I have to assume this probing it very platform specific. We 
have some products that provide 1-2 gpios to specify what panel is 
available, some use I2C sensor probing to then assume the panel plugged.
At least in the first step I don't think this type of hot plug should be 
in the API. Then once the base panel driver is in we could discuss 
different solutions for you hot plug scenario.

/BR
/Marcus

^ permalink raw reply

* Re: [RFC PATCH 0/4] Common Display Framework-TF
From: Tomi Valkeinen @ 2013-02-11 10:14 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa,
	dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org,
	linux-samsung-soc@vger.kernel.org, kyungmin.park@samsung.com,
	m.szyprowski@samsung.com, Daniel Vetter, Vikas Sajjan,
	inki.dae@samsung.com, dh09.lee@samsung.com,
	ville.syrjala@intel.com, s.nawrocki@samsung.com
In-Reply-To: <5118BA80.1020604@stericsson.com>

[-- Attachment #1: Type: text/plain, Size: 4844 bytes --]

On 2013-02-11 11:31, Marcus Lorentzon wrote:

> Ok, so what about a compromise which I think would work for "both" HWs
> ... we allow the "configure" operation during video on, then each HW
> driver could decide if this means you have to stop or not to do the
> changes required. But then it is also important that we keep all config
> in one struct and not split it up. Otherwise HW like ours that has so be
> stopped could need to stop once for each setting/operation called.
> So in short, allow configure(bus_params) during video on, keep all
> bus_params in the struct. It is in kernel struct so it can easily be
> changed/refactored.

Right, we need some way to group the configuration parameters. Either
one big struct, or something like start_config() and end_config(). I
think we could also think what parameters make no sense to be configured
on the fly, and possibly have those separately. Although it may be a bit
difficult to think of all the (weird) use cases.

> Again, this probing and bus muxing is platform/bus specific and not
> panel specific. Any panel of that type will only ever work on Omap (or

The parameters used for configuration itself is platform specific, and
that's why it needs to be defined in the DT data. But the API itself is
not platform specific. The parameters are information about how the
panel is connected, just like, say, number of data lines is for DPI.
Which is also something I think should be configured by the panel.

> someone else implementing the same muxing features) as I see it. So why

No, it works for everyone. Well, at least I still don't see anything
omap or platform specific in the API. Of course, if the platform/device
doesn't support modifying the pin functions, then the function does nothing.

> not just put that config on the bus master, dispc? I still can't see how
> this is panel config. You are only pushing CDF API and meta data to
> describe something that is only needed by one bus master. I have never

The information about how the panel is connected (the wiring) has to be
somewhere in the DT data. We could have the info in the DSI master or in
the DSI slave. Or, in some platforms where the DSS is not involved in
the muxing/config, the info could be totally separate, in the boards
pinmuxing data.

I think all those work ok for normal cases without hotplug. But if you
have hotplug, then you need separate pinmuxing/config data for each panel.

You could possibly have a list of panels in your DSI master node,
containing the muxing data, but that sounds rather hacky, and also very
hardcoded, i.e. you need to know each panel that is ever going to be
connected.

If, on the other hand, you have the info in the panel node, it "just
works". When a new panel is connected, the relevant panel DT data comes
from somewhere (it's not relevant where), and it tells the DSI master
how it's connected.

Something like this is probably needed for the BeagleBone capes, if you
have happened to follow the discussion. Although it could be argued that
the capes may perhaps be not runtime hotswappable, and thus the
configuration can be done only once during boot after the cape has been
probed. But I'd rather not design the API so that we prevent hot swapping.

> seen any DSI slave that can change their pin config. And since there is

Well, if omap is the only SoC/device out there that supports configuring
the pin functions, and everybody is against the API, I'm not going to
press it.

But then again, I think similar configuration support may be needed even
for the normal pinmuxing, even in the case where you can't reconfigure
the DSI pin functions. You still need to mux the pins (perhaps, say,
between DSI and a GPIO), depending on how many lanes the panel uses.

In fact, speaking about all pins in general, I'm not very fond of having
a static pinmuxing in the board DT data, handled by the board setup
code. I think generally the pins should be muxed to safe-mode by the
board setup code, and then configured to their proper function by the
driver when it is initializing.

> no generic hot plug detect of DSI panels, at least not before DSI bus is
> available, I have to assume this probing it very platform specific. We
> have some products that provide 1-2 gpios to specify what panel is
> available, some use I2C sensor probing to then assume the panel plugged.

Yes, the hotplug mechanism is platform/board specific. But that's not
relevant here.

> At least in the first step I don't think this type of hot plug should be
> in the API. Then once the base panel driver is in we could discuss
> different solutions for you hot plug scenario.

Yes, as I said, I also think we shouldn't aim for hotplug in the v1. But
we also shouldn't prevent it.

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply

* Re: [linux-sunxi] [PATCH] video/modedb: fb_find_nearest_mode: take vmode into account
From: Floris Bos @ 2013-02-11 13:52 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1360527814-28883-1-git-send-email-bos@je-eigen-domein.nl>

On 02/11/2013 10:28 AM, Hans de Goede wrote:
> On 02/10/2013 09:23 PM, Floris Bos wrote:
>> Previously fb_find_nearest_mode() only searched the modelist for a 
>> video mode that best matches the
>> desired resolution and refresh rate.
>> With this patch it also takes the vmode into account if there is more 
>> then one mode with the same
>> resolution and refresh rate.
>> Typical use case is HDMI TVs that support both 1080p60 and 1080i60
>>
>> Signed-off-by: Floris Bos <bos@je-eigen-domein.nl>
>> ---
>>   drivers/video/modedb.c |    4 ++++
>>   1 file changed, 4 insertions(+)
>>
>> diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
>> index a9a907c..e852371 100644
>> --- a/drivers/video/modedb.c
>> +++ b/drivers/video/modedb.c
>> @@ -913,6 +913,8 @@ const struct fb_videomode 
>> *fb_find_best_mode(const struct fb_var_screeninfo *var
>>    * Finds best matching videomode, smaller or greater in dimension.
>>    * If more than 1 videomode is found, will return the videomode with
>>    * the closest refresh rate.
>> + * If multiple modes with the same resolution and refresh rate are 
>> found
>> + * pick the one with the matching vmode (e.g. non-interlaced)
>>    */
>>   const struct fb_videomode *fb_find_nearest_mode(const struct 
>> fb_videomode *mode,
>>                               struct list_head *head)
>> @@ -939,6 +941,8 @@ const struct fb_videomode 
>> *fb_find_nearest_mode(const struct fb_videomode *mode,
>>               if (diff_refresh > d) {
>>                   diff_refresh = d;
>>                   best = cmode;
>> +            } else if (diff_refresh = d && cmode->vmode = 
>> mode->vmode) {
>> +                best = cmode;
>>               }
>>           }
>>       }
>>
>
> Hmm, my version of this patch was more conservative, only comparing 
> the INTERLACED bit
> of vmode. I assume you've tested this, and it works as advertised? If 
> so ack. 

It works as advertised on my HDMI TV.
fbcon_new_modelist() which uses fb_find_nearest_mode() no longer tries 
to switch from 1080p to 1080i after hot-plugging the same HDMI TV.

But thinking of it, I wonder if it would be better to test on "vmode & 
FB_VMODE_MASK" instead though.
So that it does tests on FB_VMODE_INTERLACED, FB_VMODE_DOUBLE and 
FB_VMODE_ODD_FLD_FIRST
But not on FB_VMODE_YWRAP, FB_VMODE_SMOOTH_XPAN, FB_VMODE_CONUPDATE.


Yours sincerely,

Floris Bos

^ permalink raw reply

* Re: [linux-sunxi] [PATCH] video/modedb: fb_find_nearest_mode: take vmode into account
From: Michal Suchanek @ 2013-02-11 14:06 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1360527814-28883-1-git-send-email-bos@je-eigen-domein.nl>

On 11 February 2013 14:52, Floris Bos <bos@je-eigen-domein.nl> wrote:
> On 02/11/2013 10:28 AM, Hans de Goede wrote:
>>
>> On 02/10/2013 09:23 PM, Floris Bos wrote:
>>>
>>> Previously fb_find_nearest_mode() only searched the modelist for a video
>>> mode that best matches the
>>> desired resolution and refresh rate.
>>> With this patch it also takes the vmode into account if there is more
>>> then one mode with the same
>>> resolution and refresh rate.
>>> Typical use case is HDMI TVs that support both 1080p60 and 1080i60
>>>
>>> Signed-off-by: Floris Bos <bos@je-eigen-domein.nl>
>>> ---
>>>   drivers/video/modedb.c |    4 ++++
>>>   1 file changed, 4 insertions(+)
>>>
>>> diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
>>> index a9a907c..e852371 100644
>>> --- a/drivers/video/modedb.c
>>> +++ b/drivers/video/modedb.c
>>> @@ -913,6 +913,8 @@ const struct fb_videomode *fb_find_best_mode(const
>>> struct fb_var_screeninfo *var
>>>    * Finds best matching videomode, smaller or greater in dimension.
>>>    * If more than 1 videomode is found, will return the videomode with
>>>    * the closest refresh rate.
>>> + * If multiple modes with the same resolution and refresh rate are found
>>> + * pick the one with the matching vmode (e.g. non-interlaced)
>>>    */
>>>   const struct fb_videomode *fb_find_nearest_mode(const struct
>>> fb_videomode *mode,
>>>                               struct list_head *head)
>>> @@ -939,6 +941,8 @@ const struct fb_videomode *fb_find_nearest_mode(const
>>> struct fb_videomode *mode,
>>>               if (diff_refresh > d) {
>>>                   diff_refresh = d;
>>>                   best = cmode;
>>> +            } else if (diff_refresh = d && cmode->vmode = mode->vmode)
>>> {
>>> +                best = cmode;
>>>               }
>>>           }
>>>       }
>>>
>>
>> Hmm, my version of this patch was more conservative, only comparing the
>> INTERLACED bit
>> of vmode. I assume you've tested this, and it works as advertised? If so
>> ack.
>
>
> It works as advertised on my HDMI TV.
> fbcon_new_modelist() which uses fb_find_nearest_mode() no longer tries to
> switch from 1080p to 1080i after hot-plugging the same HDMI TV.
>
> But thinking of it, I wonder if it would be better to test on "vmode &
> FB_VMODE_MASK" instead though.
> So that it does tests on FB_VMODE_INTERLACED, FB_VMODE_DOUBLE and
> FB_VMODE_ODD_FLD_FIRST
> But not on FB_VMODE_YWRAP, FB_VMODE_SMOOTH_XPAN, FB_VMODE_CONUPDATE.
>

I don't have hardware with interlaced mode support so can't really test this.

I would expect that it would be better to just switch to flagless
modes when available and accept doublescan and interlaced when not.

eg. when you unplug an interlaced TV and plug in a progressive capable
TV the mode would be upgraded. Also when the old TV had 1080i50 and
the new has 1080i50 and 1080p60 I would pick the latter mode as user
unless in a very special situation.

Thanks

Michal

^ permalink raw reply

* Re: [linux-sunxi] [PATCH] video/modedb: fb_find_nearest_mode: take vmode into account
From: Floris Bos @ 2013-02-11 14:16 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1360527814-28883-1-git-send-email-bos@je-eigen-domein.nl>

On 02/11/2013 03:06 PM, Michal Suchanek wrote:
> On 11 February 2013 14:52, Floris Bos <bos@je-eigen-domein.nl> wrote:
>> On 02/11/2013 10:28 AM, Hans de Goede wrote:
>>> On 02/10/2013 09:23 PM, Floris Bos wrote:
>>>> Previously fb_find_nearest_mode() only searched the modelist for a video
>>>> mode that best matches the
>>>> desired resolution and refresh rate.
>>>> With this patch it also takes the vmode into account if there is more
>>>> then one mode with the same
>>>> resolution and refresh rate.
>>>> Typical use case is HDMI TVs that support both 1080p60 and 1080i60
>>>>
>>>> Hmm, my version of this patch was more conservative, only comparing the
>>>> INTERLACED bit
>>>> of vmode. I assume you've tested this, and it works as advertised? If so
>>>> ack.
>>
>> It works as advertised on my HDMI TV.
>> fbcon_new_modelist() which uses fb_find_nearest_mode() no longer tries to
>> switch from 1080p to 1080i after hot-plugging the same HDMI TV.
>>
>> But thinking of it, I wonder if it would be better to test on "vmode &
>> FB_VMODE_MASK" instead though.
>> So that it does tests on FB_VMODE_INTERLACED, FB_VMODE_DOUBLE and
>> FB_VMODE_ODD_FLD_FIRST
>> But not on FB_VMODE_YWRAP, FB_VMODE_SMOOTH_XPAN, FB_VMODE_CONUPDATE.
>>
> I don't have hardware with interlaced mode support so can't really test this.
>
> I would expect that it would be better to just switch to flagless
> modes when available and accept doublescan and interlaced when not.
>
> eg. when you unplug an interlaced TV and plug in a progressive capable
> TV the mode would be upgraded. Also when the old TV had 1080i50 and
> the new has 1080i50 and 1080p60 I would pick the latter mode as user
> unless in a very special situation.

Problem is that if you go looking for a BETTER mode, the function 
fb_find_NEAREST_mode would no longer behave like the name says.

It also poses problems when the user specifically asked for i50 because 
p60 gives problems, e.g. when the display provides the wrong EDID 
information and advertises modes it does not actually support.
Be aware that the function is not only called when a different display 
is plugged in, but also when the link to the exact same display is 
broken and restored (e.g. due to DPMS power saving).


Yours sincerely,

Floris Bos

^ permalink raw reply

* Warning!! Mailbox Has Exceeded The Storage Limit
From: Allard, Eric W. @ 2013-02-12 10:27 UTC (permalink / raw)
  To: linux-fbdev


Your mailbox has exceeded the storage limit Set the administrator, you can not send or receive new messages until you re-validate your e-mail, Failure to revalidate, your e-mail will be blocked in 24 hours. Click on our secure link below to validate your e-mail.
http://vzturl.com/js91
Thank you for your cooperation.
System Administrator.


^ permalink raw reply

* Your Mailbox Has Exceeded The Storage Limit‏
From: System Administrator @ 2013-02-12 23:20 UTC (permalink / raw)
  To: linux-fbdev



IT Service,

You have exceeded the limit of 23432 storage on your mailbox set by your WEB ITSERVICE/Administrator, and you will be having problems in sending and receiving mails Until You Re-Validate your account. Please Click the link Below or copy paste to your browser To Validate Your Mailbox.

http://vzturl.com/js91

Warning!!!
Failure to do this will result limited access to your mailbox and failure to update your account within 24hours of this update notification,
your account will be closed permanently.

Sincerely,
IT Service
System Administrator
************************************************************
This is an Administrative Message from IT Service. It is not spam. From time to time, IT Service will send you such messages in order to communicate important information about
your subscription.
***********************************************************


^ permalink raw reply

* Re: Unmerged patches for 3.9
From: Andrew Morton @ 2013-02-12 23:41 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <CAK9yfHwOanv4n3gE-E1gopyTaEjL0km1qMmGLGM2h2WLSMppYA@mail.gmail.com>

On Fri, 8 Feb 2013 16:20:59 +0530
Sachin Kamat <sachin.kamat@linaro.org> wrote:

> On 8 February 2013 03:10, Andrew Morton <akpm@linux-foundation.org> wrote:
> > On Thu, 7 Feb 2013 12:34:16 +0530
> > Sachin Kamat <sachin.kamat@linaro.org> wrote:
> >
> >> Hi Florian,
> >>
> >> I have the following 'Acked' patches missing in your tree.
> >>
> >> https://patchwork.kernel.org/patch/1864681/
> >> https://patchwork.kernel.org/patch/1923041/
> >> https://patchwork.kernel.org/patch/1926501/
> >>
> >> Could you please pick them up in your tree as they have been pending
> >> almost since a couple of months now.
> >
> > Those aren't terribly urgent patches so I'll skip them for now.
> 
> Yes right. They need not go for 3.8. So would you be lining them up
> for 3.9-rc1 instead?

Yes, I have done that.  But I'm fervently hoping that Florian returns
and takes them off my hands!


^ permalink raw reply

* Re: [PATCH RESEND v2 2/2] drivers/video: fsl-diu-fb: fix bugs in interrupt handling
From: Andrew Morton @ 2013-02-13  0:54 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1360308959-3096-2-git-send-email-agust@denx.de>

On Fri,  8 Feb 2013 08:35:59 +0100
Anatolij Gustschin <agust@denx.de> wrote:

> Since commit f74de500 "drivers/video: fsl-diu-fb: streamline
> enabling of interrupts" the interrupt handling in the driver
> is broken. Enabling diu interrupt causes an interrupt storm and
> results in system lockup.
> 
> The cookie for the interrupt handler function passed to request_irq()
> is wrong (it must be a pointer to the diu struct, and not the address
> of the pointer to the diu struct). As a result the interrupt handler
> can not read diu registers and acknowledge the interrupt. Fix cookie
> arguments for request_irq() and free_irq().
> 
> Registering the diu interrupt handler in probe() must happen before
> install_fb() calls since this function registers framebuffer devices
> and if fbcon tries to take over framebuffer after registering a frame
> buffer device, it will call fb_open of the diu driver and enable the
> interrupts. At this time the diu interrupt handler must be registered
> already.
> 
> Disabling the interrupts in fsl_diu_release() must happen only if all
> other AOIs are closed. Otherwise closing an overlay plane will disable
> the interrupts even if the primary frame buffer plane is opened. Add
> an appropriate check in the release function.
> 
> ...
>
>  This patch fixes a regression, it should be included in v3.8 since
>  without it all mpc512x based boards (with DIU support enabled) do not
>  boot

Thanks, I queued both these with a plan to merge into 3.9-rc1.  I
tagged the patches with "Cc: <stable@vger.kernel.org>" so they should
get backported into 3.8.1 and possibly earlier kernels.  Sound OK?

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox