Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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		/* skip nodes without reg property */
147		if (!of_find_property(node, "reg", NULL))
148			continue;
149		of_mipi_dsi_device_add(host, node);
150	}
151
152	return 0;
153}
154EXPORT_SYMBOL(mipi_dsi_host_register);
155
156static int mipi_dsi_remove_device_fn(struct device *dev, void *priv)
157{
158	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
159
160	device_unregister(&dsi->dev);
161
162	return 0;
163}
164
165void mipi_dsi_host_unregister(struct mipi_dsi_host *host)
166{
167	device_for_each_child(host->dev, NULL, mipi_dsi_remove_device_fn);
168}
169EXPORT_SYMBOL(mipi_dsi_host_unregister);
170
171/**
172 * mipi_dsi_attach - attach a DSI device to its DSI host
173 * @dsi: DSI peripheral
174 */
175int mipi_dsi_attach(struct mipi_dsi_device *dsi)
176{
177	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
178
179	if (!ops || !ops->attach)
180		return -ENOSYS;
181
182	return ops->attach(dsi->host, dsi);
183}
184EXPORT_SYMBOL(mipi_dsi_attach);
185
186/**
187 * mipi_dsi_detach - detach a DSI device from its DSI host
188 * @dsi: DSI peripheral
189 */
190int mipi_dsi_detach(struct mipi_dsi_device *dsi)
191{
192	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
193
194	if (!ops || !ops->detach)
195		return -ENOSYS;
196
197	return ops->detach(dsi->host, dsi);
198}
199EXPORT_SYMBOL(mipi_dsi_detach);
200
201/**
202 * mipi_dsi_dcs_write - send DCS write command
203 * @dsi: DSI device
204 * @channel: virtual channel
205 * @data: pointer to the command followed by parameters
206 * @len: length of @data
207 */
208int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel,
209		       const void *data, size_t len)
210{
211	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
212	struct mipi_dsi_msg msg = {
213		.channel = channel,
214		.tx_buf = data,
215		.tx_len = len
216	};
217
218	if (!ops || !ops->transfer)
219		return -ENOSYS;
220
221	switch (len) {
222	case 0:
223		return -EINVAL;
224	case 1:
225		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
226		break;
227	case 2:
228		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
229		break;
230	default:
231		msg.type = MIPI_DSI_DCS_LONG_WRITE;
232		break;
233	}
234
235	return ops->transfer(dsi->host, &msg);
236}
237EXPORT_SYMBOL(mipi_dsi_dcs_write);
238
239/**
240 * mipi_dsi_dcs_read - send DCS read request command
241 * @dsi: DSI device
242 * @channel: virtual channel
243 * @cmd: DCS read command
244 * @data: pointer to read buffer
245 * @len: length of @data
246 *
247 * Function returns number of read bytes or error code.
248 */
249ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel,
250			  u8 cmd, void *data, size_t len)
251{
252	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
253	struct mipi_dsi_msg msg = {
254		.channel = channel,
255		.type = MIPI_DSI_DCS_READ,
256		.tx_buf = &cmd,
257		.tx_len = 1,
258		.rx_buf = data,
259		.rx_len = len
260	};
261
262	if (!ops || !ops->transfer)
263		return -ENOSYS;
264
265	return ops->transfer(dsi->host, &msg);
266}
267EXPORT_SYMBOL(mipi_dsi_dcs_read);
268
269static int mipi_dsi_drv_probe(struct device *dev)
270{
271	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
272	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
273
274	return drv->probe(dsi);
275}
276
277static int mipi_dsi_drv_remove(struct device *dev)
278{
279	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
280	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
281
282	return drv->remove(dsi);
283}
284
285/**
286 * mipi_dsi_driver_register - register a driver for DSI devices
287 * @drv: DSI driver structure
288 */
289int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
290{
291	drv->driver.bus = &mipi_dsi_bus_type;
292	if (drv->probe)
293		drv->driver.probe = mipi_dsi_drv_probe;
294	if (drv->remove)
295		drv->driver.remove = mipi_dsi_drv_remove;
296
297	return driver_register(&drv->driver);
298}
299EXPORT_SYMBOL(mipi_dsi_driver_register);
300
301/**
302 * mipi_dsi_driver_unregister - unregister a driver for DSI devices
303 * @drv: DSI driver structure
304 */
305void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
306{
307	driver_unregister(&drv->driver);
308}
309EXPORT_SYMBOL(mipi_dsi_driver_unregister);
310
311static int __init mipi_dsi_bus_init(void)
312{
313	return bus_register(&mipi_dsi_bus_type);
314}
315postcore_initcall(mipi_dsi_bus_init);
316
317MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
318MODULE_DESCRIPTION("MIPI DSI Bus");
319MODULE_LICENSE("GPL and additional rights");