aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
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 /drivers/gpu/drm
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>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/Kconfig4
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/drm_mipi_dsi.c315
3 files changed, 320 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");