[RFC,1/4] mipi-dsi-bus: add MIPI DSI bus support

Message ID 1380032596-18612-2-git-send-email-a.hajda@samsung.com (mailing list archive)
State RFC, archived
Headers

Commit Message

Andrzej Hajda Sept. 24, 2013, 2:23 p.m. UTC
  MIPI DSI is a high-speed serial interface to transmit
data from/to host to display module.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig        |   4 +
 drivers/video/display/Makefile       |   1 +
 drivers/video/display/mipi-dsi-bus.c | 332 +++++++++++++++++++++++++++++++++++
 include/video/display.h              |   3 +
 include/video/mipi-dsi-bus.h         | 144 +++++++++++++++
 5 files changed, 484 insertions(+)
 create mode 100644 drivers/video/display/mipi-dsi-bus.c
 create mode 100644 include/video/mipi-dsi-bus.h
  

Comments

Bert Kenward Oct. 7, 2013, 10:47 a.m. UTC | #1
On Tuesday September 24 2013 at 15:23, Andrzej Hajda wrote:
> MIPI DSI is a high-speed serial interface to transmit

> data from/to host to display module.

> 

> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>

> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

> ---

>  drivers/video/display/Kconfig        |   4 +

>  drivers/video/display/Makefile       |   1 +

>  drivers/video/display/mipi-dsi-bus.c | 332

> +++++++++++++++++++++++++++++++++++

>  include/video/display.h              |   3 +

>  include/video/mipi-dsi-bus.h         | 144 +++++++++++++++

>  5 files changed, 484 insertions(+)


<snipped as far as mipi-dsi-bus.h

> diff --git a/include/video/mipi-dsi-bus.h b/include/video/mipi-dsi-bus.h

> new file mode 100644

> index 0000000..a78792d

> --- /dev/null

> +++ b/include/video/mipi-dsi-bus.h

> @@ -0,0 +1,144 @@

> +/*

> + * MIPI DSI Bus

> + *

> + * Copyright (C) 2013, Samsung Electronics, Co., Ltd.

> + * Andrzej Hajda <a.hajda@samsung.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.

> + */

> +

> +#ifndef __MIPI_DSI_BUS_H__

> +#define __MIPI_DSI_BUS_H__

> +

> +#include <linux/device.h>

> +#include <video/videomode.h>

> +

> +struct mipi_dsi_bus;

> +struct mipi_dsi_device;

> +

> +struct mipi_dsi_bus_ops {

> +	int (*set_power)(struct mipi_dsi_bus *bus, struct mipi_dsi_device

> *dev,

> +			 bool on);

> +	int (*set_stream)(struct mipi_dsi_bus *bus, struct mipi_dsi_device

> *dev,

> +			  bool on);

> +	int (*transfer)(struct mipi_dsi_bus *bus, struct mipi_dsi_device

> *dev,

> +			u8 type, const u8 *tx_buf, size_t tx_len, u8 *rx_buf,

> +			size_t rx_len);

> +};

> +

> +#define DSI_MODE_VIDEO			(1 << 0)

> +#define DSI_MODE_VIDEO_BURST		(1 << 1)

> +#define DSI_MODE_VIDEO_SYNC_PULSE	(1 << 2)

> +#define DSI_MODE_VIDEO_AUTO_VERT	(1 << 3)

> +#define DSI_MODE_VIDEO_HSE		(1 << 4)

> +#define DSI_MODE_VIDEO_HFP		(1 << 5)

> +#define DSI_MODE_VIDEO_HBP		(1 << 6)

> +#define DSI_MODE_VIDEO_HSA		(1 << 7)

> +#define DSI_MODE_VSYNC_FLUSH		(1 << 8)

> +#define DSI_MODE_EOT_PACKET		(1 << 9)

> +

> +enum mipi_dsi_pixel_format {

> +	DSI_FMT_RGB888,

> +	DSI_FMT_RGB666,

> +	DSI_FMT_RGB666_PACKED,

> +	DSI_FMT_RGB565,

> +};

> +

> +struct mipi_dsi_interface_params {

> +	enum mipi_dsi_pixel_format format;

> +	unsigned long mode;

> +	unsigned long hs_clk_freq;

> +	unsigned long esc_clk_freq;

> +	unsigned char data_lanes;

> +	unsigned char cmd_allow;

> +};

> +

> +struct mipi_dsi_bus {

> +	struct device *dev;

> +	const struct mipi_dsi_bus_ops *ops;

> +};

> +

> +#define MIPI_DSI_MODULE_PREFIX		"mipi-dsi:"

> +#define MIPI_DSI_NAME_SIZE		32

> +

> +struct mipi_dsi_device_id {

> +	char name[MIPI_DSI_NAME_SIZE];

> +	__kernel_ulong_t driver_data	/* Data private to the driver */

> +			__aligned(sizeof(__kernel_ulong_t));

> +};

> +

> +struct mipi_dsi_device {

> +	char name[MIPI_DSI_NAME_SIZE];

> +	int id;

> +	struct device dev;

> +

> +	const struct mipi_dsi_device_id *id_entry;

> +	struct mipi_dsi_bus *bus;

> +	struct videomode vm;

> +	struct mipi_dsi_interface_params params;

> +};

> +

> +#define to_mipi_dsi_device(d)	container_of(d, struct

> mipi_dsi_device, dev)

> +

> +int mipi_dsi_device_register(struct mipi_dsi_device *dev,

> +			     struct mipi_dsi_bus *bus);

> +void mipi_dsi_device_unregister(struct mipi_dsi_device *dev);

> +

> +struct mipi_dsi_driver {

> +	int(*probe)(struct mipi_dsi_device *);

> +	int(*remove)(struct mipi_dsi_device *);

> +	struct device_driver driver;

> +	const struct mipi_dsi_device_id *id_table;

> +};

> +

> +#define to_mipi_dsi_driver(d)	container_of(d, struct

> mipi_dsi_driver, driver)

> +

> +int mipi_dsi_driver_register(struct mipi_dsi_driver *drv);

> +void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv);

> +

> +static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device

> *dev)

> +{

> +	return dev_get_drvdata(&dev->dev);

> +}

> +

> +static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dev,

> +					void *data)

> +{

> +	dev_set_drvdata(&dev->dev, data);

> +}

> +

> +int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus);

> +void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus);

> +

> +/* module_mipi_dsi_driver() - Helper macro for drivers that don't do

> + * anything special in module init/exit.  This eliminates a lot of

> + * boilerplate.  Each module may only use this macro once, and

> + * calling it replaces module_init() and module_exit()

> + */

> +#define module_mipi_dsi_driver(__mipi_dsi_driver) \

> +	module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \

> +			mipi_dsi_driver_unregister)

> +

> +int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on);

> +int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on);

> +int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8

> *data,

> +		       size_t len);

> +int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,

> +		      u8 *data, size_t len);

> +

> +#define mipi_dsi_dcs_write_seq(dev, channel, seq...) \

> +({\

> +	const u8 d[] = { seq };\

> +	BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too long for

> stack");\

> +	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\

> +})

> +

> +#define mipi_dsi_dcs_write_static_seq(dev, channel, seq...) \

> +({\

> +	static const u8 d[] = { seq };\

> +	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\

> +})

> +

> +#endif /* __MIPI_DSI_BUS__ */


I may well have missed something, but I can't see exactly how a command mode
update would be done with this interface. Would this require repeated calls to
.transfer? Such transfers would need to be flagged as requiring
synchronisation with a tearing effect control signal - either the inband
method or a dedicated line. I suspect many hardware implementations will have
a specific method for transferring pixel data in a DSI command mode transfer.

The command sending period during video mode should probably be configurable
on a per-transfer basis. Some commands have to be synchronised with vertical
blanking, others do not. This could perhaps be combined with a wider
configuration option for a given panel or interface. Similarly, selection of
low power (LP) and high speed (HS) mode on a per-transfer basis can be needed
for some panels.

Is there a mechanism for controlling ultra-low power state (ULPS) entry? Also,
is there a method for sending arbitrary trigger messages (eg the reset
trigger)?

Thanks,

Bert.
-- 
Bert Kenward
Software Engineer
Broadcom Mobile Platform Solutions
Cambridge, UK
  
Andrzej Hajda Oct. 10, 2013, 9:57 a.m. UTC | #2
On 10/07/2013 12:47 PM, Bert Kenward wrote:
> On Tuesday September 24 2013 at 15:23, Andrzej Hajda wrote:
>> MIPI DSI is a high-speed serial interface to transmit
>> data from/to host to display module.
>>
>> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  drivers/video/display/Kconfig        |   4 +
>>  drivers/video/display/Makefile       |   1 +
>>  drivers/video/display/mipi-dsi-bus.c | 332
>> +++++++++++++++++++++++++++++++++++
>>  include/video/display.h              |   3 +
>>  include/video/mipi-dsi-bus.h         | 144 +++++++++++++++
>>  5 files changed, 484 insertions(+)
> <snipped as far as mipi-dsi-bus.h
>
>> diff --git a/include/video/mipi-dsi-bus.h b/include/video/mipi-dsi-bus.h
>> new file mode 100644
>> index 0000000..a78792d
>> --- /dev/null
>> +++ b/include/video/mipi-dsi-bus.h
>> @@ -0,0 +1,144 @@
>> +/*
>> + * MIPI DSI Bus
>> + *
>> + * Copyright (C) 2013, Samsung Electronics, Co., Ltd.
>> + * Andrzej Hajda <a.hajda@samsung.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.
>> + */
>> +
>> +#ifndef __MIPI_DSI_BUS_H__
>> +#define __MIPI_DSI_BUS_H__
>> +
>> +#include <linux/device.h>
>> +#include <video/videomode.h>
>> +
>> +struct mipi_dsi_bus;
>> +struct mipi_dsi_device;
>> +
>> +struct mipi_dsi_bus_ops {
>> +	int (*set_power)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
>> *dev,
>> +			 bool on);
>> +	int (*set_stream)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
>> *dev,
>> +			  bool on);
>> +	int (*transfer)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
>> *dev,
>> +			u8 type, const u8 *tx_buf, size_t tx_len, u8 *rx_buf,
>> +			size_t rx_len);
>> +};
>> +
>> +#define DSI_MODE_VIDEO			(1 << 0)
>> +#define DSI_MODE_VIDEO_BURST		(1 << 1)
>> +#define DSI_MODE_VIDEO_SYNC_PULSE	(1 << 2)
>> +#define DSI_MODE_VIDEO_AUTO_VERT	(1 << 3)
>> +#define DSI_MODE_VIDEO_HSE		(1 << 4)
>> +#define DSI_MODE_VIDEO_HFP		(1 << 5)
>> +#define DSI_MODE_VIDEO_HBP		(1 << 6)
>> +#define DSI_MODE_VIDEO_HSA		(1 << 7)
>> +#define DSI_MODE_VSYNC_FLUSH		(1 << 8)
>> +#define DSI_MODE_EOT_PACKET		(1 << 9)
>> +
>> +enum mipi_dsi_pixel_format {
>> +	DSI_FMT_RGB888,
>> +	DSI_FMT_RGB666,
>> +	DSI_FMT_RGB666_PACKED,
>> +	DSI_FMT_RGB565,
>> +};
>> +
>> +struct mipi_dsi_interface_params {
>> +	enum mipi_dsi_pixel_format format;
>> +	unsigned long mode;
>> +	unsigned long hs_clk_freq;
>> +	unsigned long esc_clk_freq;
>> +	unsigned char data_lanes;
>> +	unsigned char cmd_allow;
>> +};
>> +
>> +struct mipi_dsi_bus {
>> +	struct device *dev;
>> +	const struct mipi_dsi_bus_ops *ops;
>> +};
>> +
>> +#define MIPI_DSI_MODULE_PREFIX		"mipi-dsi:"
>> +#define MIPI_DSI_NAME_SIZE		32
>> +
>> +struct mipi_dsi_device_id {
>> +	char name[MIPI_DSI_NAME_SIZE];
>> +	__kernel_ulong_t driver_data	/* Data private to the driver */
>> +			__aligned(sizeof(__kernel_ulong_t));
>> +};
>> +
>> +struct mipi_dsi_device {
>> +	char name[MIPI_DSI_NAME_SIZE];
>> +	int id;
>> +	struct device dev;
>> +
>> +	const struct mipi_dsi_device_id *id_entry;
>> +	struct mipi_dsi_bus *bus;
>> +	struct videomode vm;
>> +	struct mipi_dsi_interface_params params;
>> +};
>> +
>> +#define to_mipi_dsi_device(d)	container_of(d, struct
>> mipi_dsi_device, dev)
>> +
>> +int mipi_dsi_device_register(struct mipi_dsi_device *dev,
>> +			     struct mipi_dsi_bus *bus);
>> +void mipi_dsi_device_unregister(struct mipi_dsi_device *dev);
>> +
>> +struct mipi_dsi_driver {
>> +	int(*probe)(struct mipi_dsi_device *);
>> +	int(*remove)(struct mipi_dsi_device *);
>> +	struct device_driver driver;
>> +	const struct mipi_dsi_device_id *id_table;
>> +};
>> +
>> +#define to_mipi_dsi_driver(d)	container_of(d, struct
>> mipi_dsi_driver, driver)
>> +
>> +int mipi_dsi_driver_register(struct mipi_dsi_driver *drv);
>> +void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv);
>> +
>> +static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device
>> *dev)
>> +{
>> +	return dev_get_drvdata(&dev->dev);
>> +}
>> +
>> +static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dev,
>> +					void *data)
>> +{
>> +	dev_set_drvdata(&dev->dev, data);
>> +}
>> +
>> +int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus);
>> +void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus);
>> +
>> +/* module_mipi_dsi_driver() - Helper macro for drivers that don't do
>> + * anything special in module init/exit.  This eliminates a lot of
>> + * boilerplate.  Each module may only use this macro once, and
>> + * calling it replaces module_init() and module_exit()
>> + */
>> +#define module_mipi_dsi_driver(__mipi_dsi_driver) \
>> +	module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
>> +			mipi_dsi_driver_unregister)
>> +
>> +int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on);
>> +int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on);
>> +int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8
>> *data,
>> +		       size_t len);
>> +int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
>> +		      u8 *data, size_t len);
>> +
>> +#define mipi_dsi_dcs_write_seq(dev, channel, seq...) \
>> +({\
>> +	const u8 d[] = { seq };\
>> +	BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too long for
>> stack");\
>> +	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
>> +})
>> +
>> +#define mipi_dsi_dcs_write_static_seq(dev, channel, seq...) \
>> +({\
>> +	static const u8 d[] = { seq };\
>> +	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
>> +})
>> +
>> +#endif /* __MIPI_DSI_BUS__ */
> I may well have missed something,
>  but I can't see exactly how a command mode
> update would be done with this interface. Would this require repeated calls to
> .transfer? Such transfers would need to be flagged as requiring
> synchronisation with a tearing effect control signal - either the inband
> method or a dedicated line. I suspect many hardware implementations will have
> a specific method for transferring pixel data in a DSI command mode transfer.
>
> The command sending period during video mode should probably be configurable
> on a per-transfer basis. Some commands have to be synchronised with vertical
> blanking, others do not. This could perhaps be combined with a wider
> configuration option for a given panel or interface. Similarly, selection of
> low power (LP) and high speed (HS) mode on a per-transfer basis can be needed
> for some panels.
>
> Is there a mechanism for controlling ultra-low power state (ULPS) entry? Also,
> is there a method for sending arbitrary trigger messages (eg the reset
> trigger)?
Thanks for the feedback.
The current dsi bus implementation was just made to work with the
hw I have. It should be extended to be more generic, but I hope
now it is just matter of adding good opses and parameters.
Feel free to propose new opses.

Andrzej
>
> Thanks,
>
> Bert.

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  

Patch

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 9b482a8..619b05d 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -20,6 +20,10 @@  config DISPLAY_MIPI_DBI
 	tristate
 	default n
 
+config DISPLAY_MIPI_DSI
+	tristate
+	default n
+
 config DISPLAY_PANEL_DPI
 	tristate "DPI (Parallel) Display Panels"
 	---help---
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index d03c64a..b323fd4 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -3,6 +3,7 @@  display-y					:= display-core.o \
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
 obj-$(CONFIG_DISPLAY_CONNECTOR_VGA)		+= con-vga.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
+obj-$(CONFIG_DISPLAY_MIPI_DSI)			+= mipi-dsi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
 obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
diff --git a/drivers/video/display/mipi-dsi-bus.c b/drivers/video/display/mipi-dsi-bus.c
new file mode 100644
index 0000000..a194d92
--- /dev/null
+++ b/drivers/video/display/mipi-dsi-bus.c
@@ -0,0 +1,332 @@ 
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2012, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <video/mipi_display.h>
+#include <video/mipi-dsi-bus.h>
+
+/* -----------------------------------------------------------------------------
+ * Bus operations
+ */
+
+int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+
+	if (!ops->set_power)
+		return 0;
+
+	return ops->set_power(dev->bus, dev, on);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_set_power);
+
+int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+
+	if (!ops->set_stream)
+		return 0;
+
+	return ops->set_stream(dev->bus, dev, on);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_set_stream);
+
+int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8 *data,
+		       size_t len)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+	u8 type = channel << 6;
+
+	if (!ops->transfer)
+		return -EINVAL;
+
+	switch (len) {
+	case 0:
+		return -EINVAL;
+	case 1:
+		type |= MIPI_DSI_DCS_SHORT_WRITE;
+		break;
+	case 2:
+		type |= MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		break;
+	default:
+		type |= MIPI_DSI_DCS_LONG_WRITE;
+	}
+
+	return ops->transfer(dev->bus, dev, type, data, len, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_dcs_write);
+
+int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
+		      u8 *data, size_t len)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+	u8 type = MIPI_DSI_DCS_READ | (channel << 6);
+
+	if (!ops->transfer)
+		return -EINVAL;
+
+	return ops->transfer(dev->bus, dev, type, &cmd, 1, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_dcs_read);
+
+/* -----------------------------------------------------------------------------
+ * Bus type
+ */
+
+static const struct mipi_dsi_device_id *
+mipi_dsi_match_id(const struct mipi_dsi_device_id *id,
+		  struct mipi_dsi_device *dev)
+{
+	while (id->name[0]) {
+		if (strcmp(dev->name, id->name) == 0) {
+			dev->id_entry = id;
+			return id;
+		}
+		id++;
+	}
+	return NULL;
+}
+
+static int mipi_dsi_match(struct device *_dev, struct device_driver *_drv)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_drv);
+
+	if (of_driver_match_device(_dev, _drv))
+		return 1;
+
+	if (drv->id_table)
+		return mipi_dsi_match_id(drv->id_table, dev) != NULL;
+
+	return (strcmp(dev->name, _drv->name) == 0);
+}
+
+static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
+			     char *buf)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	int len = snprintf(buf, PAGE_SIZE, MIPI_DSI_MODULE_PREFIX "%s\n",
+			   dev->name);
+
+	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute mipi_dsi_dev_attrs[] = {
+	__ATTR_RO(modalias),
+	__ATTR_NULL,
+};
+
+static int mipi_dsi_uevent(struct device *_dev, struct kobj_uevent_env *env)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	add_uevent_var(env, "MODALIAS=%s%s", MIPI_DSI_MODULE_PREFIX,
+		       dev->name);
+	return 0;
+}
+
+static const struct dev_pm_ops mipi_dsi_dev_pm_ops = {
+	.runtime_suspend = pm_generic_runtime_suspend,
+	.runtime_resume = pm_generic_runtime_resume,
+	.suspend = pm_generic_suspend,
+	.resume = pm_generic_resume,
+	.freeze = pm_generic_freeze,
+	.thaw = pm_generic_thaw,
+	.poweroff = pm_generic_poweroff,
+	.restore = pm_generic_restore,
+};
+
+static struct bus_type mipi_dsi_bus_type = {
+	.name		= "mipi-dsi",
+	.dev_attrs	= mipi_dsi_dev_attrs,
+	.match		= mipi_dsi_match,
+	.uevent		= mipi_dsi_uevent,
+	.pm		= &mipi_dsi_dev_pm_ops,
+};
+
+void mipi_dsi_dev_release(struct device *_dev)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	kfree(dev);
+}
+
+static struct device_type mipi_dsi_dev_type = {
+	.release	= mipi_dsi_dev_release,
+};
+
+
+/* -----------------------------------------------------------------------------
+ * Device and driver (un)registration
+ */
+
+/**
+ * mipi_dsi_device_register - register a DSI device
+ * @dev: DSI device we're registering
+ */
+int mipi_dsi_device_register(struct mipi_dsi_device *dev,
+			      struct mipi_dsi_bus *bus)
+{
+	device_initialize(&dev->dev);
+
+	dev->bus = bus;
+	dev->dev.bus = &mipi_dsi_bus_type;
+	dev->dev.parent = bus->dev;
+	dev->dev.type = &mipi_dsi_dev_type;
+
+	if (dev->id != -1)
+		dev_set_name(&dev->dev, "%s.%d", dev->name,  dev->id);
+	else
+		dev_set_name(&dev->dev, "%s", dev->name);
+
+	return device_add(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_device_register);
+
+/**
+ * mipi_dsi_device_unregister - unregister a DSI device
+ * @dev: DSI device we're unregistering
+ */
+void mipi_dsi_device_unregister(struct mipi_dsi_device *dev)
+{
+	device_del(&dev->dev);
+	put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_device_unregister);
+
+static int mipi_dsi_drv_probe(struct device *_dev)
+{
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_dev->driver);
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	return drv->probe(dev);
+}
+
+static int mipi_dsi_drv_remove(struct device *_dev)
+{
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_dev->driver);
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	return drv->remove(dev);
+}
+
+/**
+ * mipi_dsi_driver_register - register a driver for DSI devices
+ * @drv: DSI driver structure
+ */
+int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
+{
+	drv->driver.bus = &mipi_dsi_bus_type;
+	if (drv->probe)
+		drv->driver.probe = mipi_dsi_drv_probe;
+	if (drv->remove)
+		drv->driver.remove = mipi_dsi_drv_remove;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_driver_register);
+
+/**
+ * mipi_dsi_driver_unregister - unregister a driver for DSI devices
+ * @drv: DSI driver structure
+ */
+void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_driver_unregister);
+
+struct mipi_dsi_device *of_mipi_dsi_register_device(struct mipi_dsi_bus *bus,
+						    struct device_node *node)
+{
+	struct mipi_dsi_device *d = NULL;
+	int ret;
+
+	d = kzalloc(sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return ERR_PTR(-ENOMEM);
+
+	ret = of_modalias_node(node, d->name, sizeof(d->name));
+	if (ret) {
+		dev_err(bus->dev, "modalias failure on %s\n", node->full_name);
+		goto err_free;
+	}
+
+	d->dev.of_node = of_node_get(node);
+	if (!d->dev.of_node)
+		goto err_free;
+
+	ret = mipi_dsi_device_register(d, bus);
+
+	if (!ret)
+		return d;
+
+	of_node_put(node);
+err_free:
+	kfree(d);
+
+	return ERR_PTR(ret);
+}
+
+int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus)
+{
+	struct device_node *n;
+
+	for_each_child_of_node(bus->dev->of_node, n)
+		of_mipi_dsi_register_device(bus, n);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_mipi_dsi_register_devices);
+
+static int mipi_dsi_remove_device_fn(struct device *_dev, void *priv)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	mipi_dsi_device_unregister(dev);
+
+	return 0;
+}
+
+void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus)
+{
+	device_for_each_child(bus->dev, bus, mipi_dsi_remove_device_fn);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_unregister_devices);
+/* -----------------------------------------------------------------------------
+ * Init/exit
+ */
+
+static int __init mipi_dsi_init(void)
+{
+	return bus_register(&mipi_dsi_bus_type);
+}
+
+static void __exit mipi_dsi_exit(void)
+{
+	bus_unregister(&mipi_dsi_bus_type);
+}
+
+module_init(mipi_dsi_init);
+module_exit(mipi_dsi_exit)
+
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("MIPI DSI Bus");
+MODULE_LICENSE("GPL v2");
diff --git a/include/video/display.h b/include/video/display.h
index 3138401..7faca0f 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -18,6 +18,7 @@ 
 #include <linux/module.h>
 #include <media/media-entity.h>
 #include <video/mipi-dbi-bus.h>
+#include <video/mipi-dsi-bus.h>
 
 #define DISPLAY_PIXEL_CODING(option, type, from, to, variant) \
 	(((option) << 17) | ((type) << 13) | ((variant) << 10) | \
@@ -184,6 +185,7 @@  enum display_entity_stream_state {
 enum display_entity_interface_type {
 	DISPLAY_ENTITY_INTERFACE_DPI,
 	DISPLAY_ENTITY_INTERFACE_DBI,
+	DISPLAY_ENTITY_INTERFACE_DSI,
 	DISPLAY_ENTITY_INTERFACE_LVDS,
 	DISPLAY_ENTITY_INTERFACE_VGA,
 };
@@ -192,6 +194,7 @@  struct display_entity_interface_params {
 	enum display_entity_interface_type type;
 	union {
 		struct mipi_dbi_interface_params dbi;
+		struct mipi_dsi_interface_params dsi;
 	} p;
 };
 
diff --git a/include/video/mipi-dsi-bus.h b/include/video/mipi-dsi-bus.h
new file mode 100644
index 0000000..a78792d
--- /dev/null
+++ b/include/video/mipi-dsi-bus.h
@@ -0,0 +1,144 @@ 
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2013, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.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.
+ */
+
+#ifndef __MIPI_DSI_BUS_H__
+#define __MIPI_DSI_BUS_H__
+
+#include <linux/device.h>
+#include <video/videomode.h>
+
+struct mipi_dsi_bus;
+struct mipi_dsi_device;
+
+struct mipi_dsi_bus_ops {
+	int (*set_power)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			 bool on);
+	int (*set_stream)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			  bool on);
+	int (*transfer)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			u8 type, const u8 *tx_buf, size_t tx_len, u8 *rx_buf,
+			size_t rx_len);
+};
+
+#define DSI_MODE_VIDEO			(1 << 0)
+#define DSI_MODE_VIDEO_BURST		(1 << 1)
+#define DSI_MODE_VIDEO_SYNC_PULSE	(1 << 2)
+#define DSI_MODE_VIDEO_AUTO_VERT	(1 << 3)
+#define DSI_MODE_VIDEO_HSE		(1 << 4)
+#define DSI_MODE_VIDEO_HFP		(1 << 5)
+#define DSI_MODE_VIDEO_HBP		(1 << 6)
+#define DSI_MODE_VIDEO_HSA		(1 << 7)
+#define DSI_MODE_VSYNC_FLUSH		(1 << 8)
+#define DSI_MODE_EOT_PACKET		(1 << 9)
+
+enum mipi_dsi_pixel_format {
+	DSI_FMT_RGB888,
+	DSI_FMT_RGB666,
+	DSI_FMT_RGB666_PACKED,
+	DSI_FMT_RGB565,
+};
+
+struct mipi_dsi_interface_params {
+	enum mipi_dsi_pixel_format format;
+	unsigned long mode;
+	unsigned long hs_clk_freq;
+	unsigned long esc_clk_freq;
+	unsigned char data_lanes;
+	unsigned char cmd_allow;
+};
+
+struct mipi_dsi_bus {
+	struct device *dev;
+	const struct mipi_dsi_bus_ops *ops;
+};
+
+#define MIPI_DSI_MODULE_PREFIX		"mipi-dsi:"
+#define MIPI_DSI_NAME_SIZE		32
+
+struct mipi_dsi_device_id {
+	char name[MIPI_DSI_NAME_SIZE];
+	__kernel_ulong_t driver_data	/* Data private to the driver */
+			__aligned(sizeof(__kernel_ulong_t));
+};
+
+struct mipi_dsi_device {
+	char name[MIPI_DSI_NAME_SIZE];
+	int id;
+	struct device dev;
+
+	const struct mipi_dsi_device_id *id_entry;
+	struct mipi_dsi_bus *bus;
+	struct videomode vm;
+	struct mipi_dsi_interface_params params;
+};
+
+#define to_mipi_dsi_device(d)	container_of(d, struct mipi_dsi_device, dev)
+
+int mipi_dsi_device_register(struct mipi_dsi_device *dev,
+			     struct mipi_dsi_bus *bus);
+void mipi_dsi_device_unregister(struct mipi_dsi_device *dev);
+
+struct mipi_dsi_driver {
+	int(*probe)(struct mipi_dsi_device *);
+	int(*remove)(struct mipi_dsi_device *);
+	struct device_driver driver;
+	const struct mipi_dsi_device_id *id_table;
+};
+
+#define to_mipi_dsi_driver(d)	container_of(d, struct mipi_dsi_driver, driver)
+
+int mipi_dsi_driver_register(struct mipi_dsi_driver *drv);
+void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv);
+
+static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dev,
+					void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus);
+void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus);
+
+/* module_mipi_dsi_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit.  This eliminates a lot of
+ * boilerplate.  Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_mipi_dsi_driver(__mipi_dsi_driver) \
+	module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
+			mipi_dsi_driver_unregister)
+
+int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on);
+int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on);
+int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8 *data,
+		       size_t len);
+int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
+		      u8 *data, size_t len);
+
+#define mipi_dsi_dcs_write_seq(dev, channel, seq...) \
+({\
+	const u8 d[] = { seq };\
+	BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too long for stack");\
+	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
+})
+
+#define mipi_dsi_dcs_write_static_seq(dev, channel, seq...) \
+({\
+	static const u8 d[] = { seq };\
+	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
+})
+
+#endif /* __MIPI_DSI_BUS__ */