aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrzej Hajda <a.hajda@samsung.com>2013-12-04 10:35:12 -0500
committerThierry Reding <treding@nvidia.com>2013-12-17 12:09:43 -0500
commit068a00233969833f1ba925e7627797489efd6041 (patch)
tree81a112ff0863c69563e61d7442ef4bad0bf68419
parentd95f95eb96c511b39bf9c854217d7dddb6d06755 (diff)
drm: Add MIPI DSI bus support
MIPI DSI bus allows to model DSI hosts and DSI peripherals using the Linux driver model. DSI hosts are registered by the DSI host drivers. During registration DSI peripherals will be created from the children of the DSI host's device tree node. Support for registration from board-setup code will be added later when needed. DSI hosts expose operations which can be used by DSI peripheral drivers to access associated devices. Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/gpu/drm/Kconfig4
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/drm_mipi_dsi.c315
-rw-r--r--include/drm/drm_mipi_dsi.h158
4 files changed, 478 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f86427591167..0b8f8fdfdea9 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -20,6 +20,10 @@ menuconfig DRM
20 details. You should also select and configure AGP 20 details. You should also select and configure AGP
21 (/dev/agpgart) support if it is available for your platform. 21 (/dev/agpgart) support if it is available for your platform.
22 22
23config DRM_MIPI_DSI
24 bool
25 depends on DRM
26
23config DRM_USB 27config DRM_USB
24 tristate 28 tristate
25 depends on DRM 29 depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index cc08b845f965..b46239a619cf 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
31CFLAGS_drm_trace_points.o := -I$(src) 31CFLAGS_drm_trace_points.o := -I$(src)
32 32
33obj-$(CONFIG_DRM) += drm.o 33obj-$(CONFIG_DRM) += drm.o
34obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
34obj-$(CONFIG_DRM_USB) += drm_usb.o 35obj-$(CONFIG_DRM_USB) += drm_usb.o
35obj-$(CONFIG_DRM_TTM) += ttm/ 36obj-$(CONFIG_DRM_TTM) += ttm/
36obj-$(CONFIG_DRM_TDFX) += tdfx/ 37obj-$(CONFIG_DRM_TDFX) += tdfx/
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
new file mode 100644
index 000000000000..b155ee2ffa17
--- /dev/null
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -0,0 +1,315 @@
1/*
2 * MIPI DSI Bus
3 *
4 * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
5 * Andrzej Hajda <a.hajda@samsung.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#include <drm/drm_mipi_dsi.h>
29
30#include <linux/device.h>
31#include <linux/module.h>
32#include <linux/of_device.h>
33#include <linux/pm_runtime.h>
34#include <linux/slab.h>
35
36#include <video/mipi_display.h>
37
38static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv)
39{
40 return of_driver_match_device(dev, drv);
41}
42
43static const struct dev_pm_ops mipi_dsi_device_pm_ops = {
44 .runtime_suspend = pm_generic_runtime_suspend,
45 .runtime_resume = pm_generic_runtime_resume,
46 .suspend = pm_generic_suspend,
47 .resume = pm_generic_resume,
48 .freeze = pm_generic_freeze,
49 .thaw = pm_generic_thaw,
50 .poweroff = pm_generic_poweroff,
51 .restore = pm_generic_restore,
52};
53
54static struct bus_type mipi_dsi_bus_type = {
55 .name = "mipi-dsi",
56 .match = mipi_dsi_device_match,
57 .pm = &mipi_dsi_device_pm_ops,
58};
59
60static void mipi_dsi_dev_release(struct device *dev)
61{
62 struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
63
64 of_node_put(dev->of_node);
65 kfree(dsi);
66}
67
68static const struct device_type mipi_dsi_device_type = {
69 .release = mipi_dsi_dev_release,
70};
71
72static struct mipi_dsi_device *mipi_dsi_device_alloc(struct mipi_dsi_host *host)
73{
74 struct mipi_dsi_device *dsi;
75
76 dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
77 if (!dsi)
78 return ERR_PTR(-ENOMEM);
79
80 dsi->host = host;
81 dsi->dev.bus = &mipi_dsi_bus_type;
82 dsi->dev.parent = host->dev;
83 dsi->dev.type = &mipi_dsi_device_type;
84
85 device_initialize(&dsi->dev);
86
87 return dsi;
88}
89
90static int mipi_dsi_device_add(struct mipi_dsi_device *dsi)
91{
92 struct mipi_dsi_host *host = dsi->host;
93
94 dev_set_name(&dsi->dev, "%s.%d", dev_name(host->dev), dsi->channel);
95
96 return device_add(&dsi->dev);
97}
98
99static struct mipi_dsi_device *
100of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node)
101{
102 struct mipi_dsi_device *dsi;
103 struct device *dev = host->dev;
104 int ret;
105 u32 reg;
106
107 ret = of_property_read_u32(node, "reg", &reg);
108 if (ret) {
109 dev_err(dev, "device node %s has no valid reg property: %d\n",
110 node->full_name, ret);
111 return ERR_PTR(-EINVAL);
112 }
113
114 if (reg > 3) {
115 dev_err(dev, "device node %s has invalid reg property: %u\n",
116 node->full_name, reg);
117 return ERR_PTR(-EINVAL);
118 }
119
120 dsi = mipi_dsi_device_alloc(host);
121 if (IS_ERR(dsi)) {
122 dev_err(dev, "failed to allocate DSI device %s: %ld\n",
123 node->full_name, PTR_ERR(dsi));
124 return dsi;
125 }
126
127 dsi->dev.of_node = of_node_get(node);
128 dsi->channel = reg;
129
130 ret = mipi_dsi_device_add(dsi);
131 if (ret) {
132 dev_err(dev, "failed to add DSI device %s: %d\n",
133 node->full_name, ret);
134 kfree(dsi);
135 return ERR_PTR(ret);
136 }
137
138 return dsi;
139}
140
141int mipi_dsi_host_register(struct mipi_dsi_host *host)
142{
143 struct device_node *node;
144
145 for_each_available_child_of_node(host->dev->of_node, node)
146 of_mipi_dsi_device_add(host, node);
147
148 return 0;
149}
150EXPORT_SYMBOL(mipi_dsi_host_register);
151
152static int mipi_dsi_remove_device_fn(struct device *dev, void *priv)
153{
154 struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
155
156 device_unregister(&dsi->dev);
157
158 return 0;
159}
160
161void mipi_dsi_host_unregister(struct mipi_dsi_host *host)
162{
163 device_for_each_child(host->dev, NULL, mipi_dsi_remove_device_fn);
164}
165EXPORT_SYMBOL(mipi_dsi_host_unregister);
166
167/**
168 * mipi_dsi_attach - attach a DSI device to its DSI host
169 * @dsi: DSI peripheral
170 */
171int mipi_dsi_attach(struct mipi_dsi_device *dsi)
172{
173 const struct mipi_dsi_host_ops *ops = dsi->host->ops;
174
175 if (!ops || !ops->attach)
176 return -ENOSYS;
177
178 return ops->attach(dsi->host, dsi);
179}
180EXPORT_SYMBOL(mipi_dsi_attach);
181
182/**
183 * mipi_dsi_detach - detach a DSI device from its DSI host
184 * @dsi: DSI peripheral
185 */
186int mipi_dsi_detach(struct mipi_dsi_device *dsi)
187{
188 const struct mipi_dsi_host_ops *ops = dsi->host->ops;
189
190 if (!ops || !ops->detach)
191 return -ENOSYS;
192
193 return ops->detach(dsi->host, dsi);
194}
195EXPORT_SYMBOL(mipi_dsi_detach);
196
197/**
198 * mipi_dsi_dcs_write - send DCS write command
199 * @dsi: DSI device
200 * @channel: virtual channel
201 * @data: pointer to the command followed by parameters
202 * @len: length of @data
203 */
204int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel,
205 const void *data, size_t len)
206{
207 const struct mipi_dsi_host_ops *ops = dsi->host->ops;
208 struct mipi_dsi_msg msg = {
209 .channel = channel,
210 .tx_buf = data,
211 .tx_len = len
212 };
213
214 if (!ops || !ops->transfer)
215 return -ENOSYS;
216
217 switch (len) {
218 case 0:
219 return -EINVAL;
220 case 1:
221 msg.type = MIPI_DSI_DCS_SHORT_WRITE;
222 break;
223 case 2:
224 msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
225 break;
226 default:
227 msg.type = MIPI_DSI_DCS_LONG_WRITE;
228 break;
229 }
230
231 return ops->transfer(dsi->host, &msg);
232}
233EXPORT_SYMBOL(mipi_dsi_dcs_write);
234
235/**
236 * mipi_dsi_dcs_read - send DCS read request command
237 * @dsi: DSI device
238 * @channel: virtual channel
239 * @cmd: DCS read command
240 * @data: pointer to read buffer
241 * @len: length of @data
242 *
243 * Function returns number of read bytes or error code.
244 */
245ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel,
246 u8 cmd, void *data, size_t len)
247{
248 const struct mipi_dsi_host_ops *ops = dsi->host->ops;
249 struct mipi_dsi_msg msg = {
250 .channel = channel,
251 .type = MIPI_DSI_DCS_READ,
252 .tx_buf = &cmd,
253 .tx_len = 1,
254 .rx_buf = data,
255 .rx_len = len
256 };
257
258 if (!ops || !ops->transfer)
259 return -ENOSYS;
260
261 return ops->transfer(dsi->host, &msg);
262}
263EXPORT_SYMBOL(mipi_dsi_dcs_read);
264
265static int mipi_dsi_drv_probe(struct device *dev)
266{
267 struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
268 struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
269
270 return drv->probe(dsi);
271}
272
273static int mipi_dsi_drv_remove(struct device *dev)
274{
275 struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
276 struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
277
278 return drv->remove(dsi);
279}
280
281/**
282 * mipi_dsi_driver_register - register a driver for DSI devices
283 * @drv: DSI driver structure
284 */
285int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
286{
287 drv->driver.bus = &mipi_dsi_bus_type;
288 if (drv->probe)
289 drv->driver.probe = mipi_dsi_drv_probe;
290 if (drv->remove)
291 drv->driver.remove = mipi_dsi_drv_remove;
292
293 return driver_register(&drv->driver);
294}
295EXPORT_SYMBOL(mipi_dsi_driver_register);
296
297/**
298 * mipi_dsi_driver_unregister - unregister a driver for DSI devices
299 * @drv: DSI driver structure
300 */
301void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
302{
303 driver_unregister(&drv->driver);
304}
305EXPORT_SYMBOL(mipi_dsi_driver_unregister);
306
307static int __init mipi_dsi_bus_init(void)
308{
309 return bus_register(&mipi_dsi_bus_type);
310}
311postcore_initcall(mipi_dsi_bus_init);
312
313MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
314MODULE_DESCRIPTION("MIPI DSI Bus");
315MODULE_LICENSE("GPL and additional rights");
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
new file mode 100644
index 000000000000..d32628acdd90
--- /dev/null
+++ b/include/drm/drm_mipi_dsi.h
@@ -0,0 +1,158 @@
1/*
2 * MIPI DSI Bus
3 *
4 * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
5 * Andrzej Hajda <a.hajda@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#ifndef __DRM_MIPI_DSI_H__
13#define __DRM_MIPI_DSI_H__
14
15#include <linux/device.h>
16
17struct mipi_dsi_host;
18struct mipi_dsi_device;
19
20/**
21 * struct mipi_dsi_msg - read/write DSI buffer
22 * @channel: virtual channel id
23 * @type: payload data type
24 * @tx_len: length of @tx_buf
25 * @tx_buf: data to be written
26 * @rx_len: length of @rx_buf
27 * @rx_buf: data to be read, or NULL
28 */
29struct mipi_dsi_msg {
30 u8 channel;
31 u8 type;
32
33 size_t tx_len;
34 const void *tx_buf;
35
36 size_t rx_len;
37 void *rx_buf;
38};
39
40/**
41 * struct mipi_dsi_host_ops - DSI bus operations
42 * @attach: attach DSI device to DSI host
43 * @detach: detach DSI device from DSI host
44 * @transfer: send and/or receive DSI packet, return number of received bytes,
45 * or error
46 */
47struct mipi_dsi_host_ops {
48 int (*attach)(struct mipi_dsi_host *host,
49 struct mipi_dsi_device *dsi);
50 int (*detach)(struct mipi_dsi_host *host,
51 struct mipi_dsi_device *dsi);
52 ssize_t (*transfer)(struct mipi_dsi_host *host,
53 struct mipi_dsi_msg *msg);
54};
55
56/**
57 * struct mipi_dsi_host - DSI host device
58 * @dev: driver model device node for this DSI host
59 * @ops: DSI host operations
60 */
61struct mipi_dsi_host {
62 struct device *dev;
63 const struct mipi_dsi_host_ops *ops;
64};
65
66int mipi_dsi_host_register(struct mipi_dsi_host *host);
67void mipi_dsi_host_unregister(struct mipi_dsi_host *host);
68
69/* DSI mode flags */
70
71/* video mode */
72#define MIPI_DSI_MODE_VIDEO BIT(0)
73/* video burst mode */
74#define MIPI_DSI_MODE_VIDEO_BURST BIT(1)
75/* video pulse mode */
76#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2)
77/* enable auto vertical count mode */
78#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3)
79/* enable hsync-end packets in vsync-pulse and v-porch area */
80#define MIPI_DSI_MODE_VIDEO_HSE BIT(4)
81/* disable hfront-porch area */
82#define MIPI_DSI_MODE_VIDEO_HFP BIT(5)
83/* disable hback-porch area */
84#define MIPI_DSI_MODE_VIDEO_HBP BIT(6)
85/* disable hsync-active area */
86#define MIPI_DSI_MODE_VIDEO_HSA BIT(7)
87/* flush display FIFO on vsync pulse */
88#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8)
89/* disable EoT packets in HS mode */
90#define MIPI_DSI_MODE_EOT_PACKET BIT(9)
91
92enum mipi_dsi_pixel_format {
93 MIPI_DSI_FMT_RGB888,
94 MIPI_DSI_FMT_RGB666,
95 MIPI_DSI_FMT_RGB666_PACKED,
96 MIPI_DSI_FMT_RGB565,
97};
98
99/**
100 * struct mipi_dsi_device - DSI peripheral device
101 * @host: DSI host for this peripheral
102 * @dev: driver model device node for this peripheral
103 * @channel: virtual channel assigned to the peripheral
104 * @format: pixel format for video mode
105 * @lanes: number of active data lanes
106 * @mode_flags: DSI operation mode related flags
107 */
108struct mipi_dsi_device {
109 struct mipi_dsi_host *host;
110 struct device dev;
111
112 unsigned int channel;
113 unsigned int lanes;
114 enum mipi_dsi_pixel_format format;
115 unsigned long mode_flags;
116};
117
118#define to_mipi_dsi_device(d) container_of(d, struct mipi_dsi_device, dev)
119
120int mipi_dsi_attach(struct mipi_dsi_device *dsi);
121int mipi_dsi_detach(struct mipi_dsi_device *dsi);
122int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel,
123 const void *data, size_t len);
124ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel,
125 u8 cmd, void *data, size_t len);
126
127/**
128 * struct mipi_dsi_driver - DSI driver
129 * @driver: device driver model driver
130 * @probe: callback for device binding
131 * @remove: callback for device unbinding
132 */
133struct mipi_dsi_driver {
134 struct device_driver driver;
135 int(*probe)(struct mipi_dsi_device *dsi);
136 int(*remove)(struct mipi_dsi_device *dsi);
137};
138
139#define to_mipi_dsi_driver(d) container_of(d, struct mipi_dsi_driver, driver)
140
141static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device *dsi)
142{
143 return dev_get_drvdata(&dsi->dev);
144}
145
146static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dsi, void *data)
147{
148 dev_set_drvdata(&dsi->dev, data);
149}
150
151int mipi_dsi_driver_register(struct mipi_dsi_driver *driver);
152void mipi_dsi_driver_unregister(struct mipi_dsi_driver *driver);
153
154#define module_mipi_dsi_driver(__mipi_dsi_driver) \
155 module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
156 mipi_dsi_driver_unregister)
157
158#endif /* __DRM_MIPI_DSI__ */