Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andrzej Hajda and committed by
Thierry Reding
068a0023 d95f95eb

+478
+4
drivers/gpu/drm/Kconfig
··· 20 20 details. You should also select and configure AGP 21 21 (/dev/agpgart) support if it is available for your platform. 22 22 23 + config DRM_MIPI_DSI 24 + bool 25 + depends on DRM 26 + 23 27 config DRM_USB 24 28 tristate 25 29 depends on DRM
+1
drivers/gpu/drm/Makefile
··· 31 31 CFLAGS_drm_trace_points.o := -I$(src) 32 32 33 33 obj-$(CONFIG_DRM) += drm.o 34 + obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o 34 35 obj-$(CONFIG_DRM_USB) += drm_usb.o 35 36 obj-$(CONFIG_DRM_TTM) += ttm/ 36 37 obj-$(CONFIG_DRM_TDFX) += tdfx/
+315
drivers/gpu/drm/drm_mipi_dsi.c
··· 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 + 38 + static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv) 39 + { 40 + return of_driver_match_device(dev, drv); 41 + } 42 + 43 + static 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 + 54 + static 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 + 60 + static 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 + 68 + static const struct device_type mipi_dsi_device_type = { 69 + .release = mipi_dsi_dev_release, 70 + }; 71 + 72 + static 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 + 90 + static 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 + 99 + static struct mipi_dsi_device * 100 + of_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 + 141 + int 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 + } 150 + EXPORT_SYMBOL(mipi_dsi_host_register); 151 + 152 + static 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 + 161 + void mipi_dsi_host_unregister(struct mipi_dsi_host *host) 162 + { 163 + device_for_each_child(host->dev, NULL, mipi_dsi_remove_device_fn); 164 + } 165 + EXPORT_SYMBOL(mipi_dsi_host_unregister); 166 + 167 + /** 168 + * mipi_dsi_attach - attach a DSI device to its DSI host 169 + * @dsi: DSI peripheral 170 + */ 171 + int 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 + } 180 + EXPORT_SYMBOL(mipi_dsi_attach); 181 + 182 + /** 183 + * mipi_dsi_detach - detach a DSI device from its DSI host 184 + * @dsi: DSI peripheral 185 + */ 186 + int 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 + } 195 + EXPORT_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 + */ 204 + int 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 + } 233 + EXPORT_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 + */ 245 + ssize_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 + } 263 + EXPORT_SYMBOL(mipi_dsi_dcs_read); 264 + 265 + static 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 + 273 + static 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 + */ 285 + int 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 + } 295 + EXPORT_SYMBOL(mipi_dsi_driver_register); 296 + 297 + /** 298 + * mipi_dsi_driver_unregister - unregister a driver for DSI devices 299 + * @drv: DSI driver structure 300 + */ 301 + void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv) 302 + { 303 + driver_unregister(&drv->driver); 304 + } 305 + EXPORT_SYMBOL(mipi_dsi_driver_unregister); 306 + 307 + static int __init mipi_dsi_bus_init(void) 308 + { 309 + return bus_register(&mipi_dsi_bus_type); 310 + } 311 + postcore_initcall(mipi_dsi_bus_init); 312 + 313 + MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); 314 + MODULE_DESCRIPTION("MIPI DSI Bus"); 315 + MODULE_LICENSE("GPL and additional rights");
+158
include/drm/drm_mipi_dsi.h
··· 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 + 17 + struct mipi_dsi_host; 18 + struct 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 + */ 29 + struct 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 + */ 47 + struct 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 + */ 61 + struct mipi_dsi_host { 62 + struct device *dev; 63 + const struct mipi_dsi_host_ops *ops; 64 + }; 65 + 66 + int mipi_dsi_host_register(struct mipi_dsi_host *host); 67 + void 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 + 92 + enum 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 + */ 108 + struct 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 + 120 + int mipi_dsi_attach(struct mipi_dsi_device *dsi); 121 + int mipi_dsi_detach(struct mipi_dsi_device *dsi); 122 + int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, 123 + const void *data, size_t len); 124 + ssize_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 + */ 133 + struct 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 + 141 + static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device *dsi) 142 + { 143 + return dev_get_drvdata(&dsi->dev); 144 + } 145 + 146 + static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dsi, void *data) 147 + { 148 + dev_set_drvdata(&dsi->dev, data); 149 + } 150 + 151 + int mipi_dsi_driver_register(struct mipi_dsi_driver *driver); 152 + void 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__ */