Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

Need help to get security updates for your Linux BSP?
Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2020 BayLibre, SAS
  4 * Author: Neil Armstrong <narmstrong@baylibre.com>
  5 */
  6
  7#include <linux/delay.h>
  8#include <linux/gpio/consumer.h>
  9#include <linux/module.h>
 10#include <linux/of.h>
 11#include <linux/regulator/consumer.h>
 12
 13#include <video/mipi_display.h>
 14
 15#include <drm/drm_crtc.h>
 16#include <drm/drm_device.h>
 17#include <drm/drm_mipi_dsi.h>
 18#include <drm/drm_modes.h>
 19#include <drm/drm_panel.h>
 20
 21struct tdo_tl070wsh30_panel {
 22	struct drm_panel base;
 23	struct mipi_dsi_device *link;
 24
 25	struct regulator *supply;
 26	struct gpio_desc *reset_gpio;
 27};
 28
 29static inline
 30struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel)
 31{
 32	return container_of(panel, struct tdo_tl070wsh30_panel, base);
 33}
 34
 35static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
 36{
 37	struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
 38	int err;
 39
 40	err = regulator_enable(tdo_tl070wsh30->supply);
 41	if (err < 0)
 42		return err;
 43
 44	usleep_range(10000, 11000);
 45
 46	gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 1);
 47
 48	usleep_range(10000, 11000);
 49
 50	gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 0);
 51
 52	msleep(200);
 53
 54	err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link);
 55	if (err < 0) {
 56		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
 57		regulator_disable(tdo_tl070wsh30->supply);
 58		return err;
 59	}
 60
 61	msleep(200);
 62
 63	err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link);
 64	if (err < 0) {
 65		dev_err(panel->dev, "failed to set display on: %d\n", err);
 66		regulator_disable(tdo_tl070wsh30->supply);
 67		return err;
 68	}
 69
 70	msleep(20);
 71
 72	return 0;
 73}
 74
 75static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel)
 76{
 77	struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
 78	int err;
 79
 80	err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link);
 81	if (err < 0)
 82		dev_err(panel->dev, "failed to set display off: %d\n", err);
 83
 84	usleep_range(10000, 11000);
 85
 86	err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link);
 87	if (err < 0) {
 88		dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
 89		return err;
 90	}
 91
 92	usleep_range(10000, 11000);
 93
 94	regulator_disable(tdo_tl070wsh30->supply);
 95
 96	return 0;
 97}
 98
 99static const struct drm_display_mode default_mode = {
100	.clock = 47250,
101	.hdisplay = 1024,
102	.hsync_start = 1024 + 46,
103	.hsync_end = 1024 + 46 + 80,
104	.htotal = 1024 + 46 + 80 + 100,
105	.vdisplay = 600,
106	.vsync_start = 600 + 5,
107	.vsync_end = 600 + 5 + 5,
108	.vtotal = 600 + 5 + 5 + 20,
109	.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
110};
111
112static int tdo_tl070wsh30_panel_get_modes(struct drm_panel *panel,
113				       struct drm_connector *connector)
114{
115	struct drm_display_mode *mode;
116
117	mode = drm_mode_duplicate(connector->dev, &default_mode);
118	if (!mode) {
119		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
120			default_mode.hdisplay, default_mode.vdisplay,
121			drm_mode_vrefresh(&default_mode));
122		return -ENOMEM;
123	}
124
125	drm_mode_set_name(mode);
126
127	drm_mode_probed_add(connector, mode);
128
129	connector->display_info.width_mm = 154;
130	connector->display_info.height_mm = 85;
131	connector->display_info.bpc = 8;
132
133	return 1;
134}
135
136static const struct drm_panel_funcs tdo_tl070wsh30_panel_funcs = {
137	.unprepare = tdo_tl070wsh30_panel_unprepare,
138	.prepare = tdo_tl070wsh30_panel_prepare,
139	.get_modes = tdo_tl070wsh30_panel_get_modes,
140};
141
142static const struct of_device_id tdo_tl070wsh30_of_match[] = {
143	{ .compatible = "tdo,tl070wsh30", },
144	{ /* sentinel */ }
145};
146MODULE_DEVICE_TABLE(of, tdo_tl070wsh30_of_match);
147
148static int tdo_tl070wsh30_panel_add(struct tdo_tl070wsh30_panel *tdo_tl070wsh30)
149{
150	struct device *dev = &tdo_tl070wsh30->link->dev;
151	int err;
152
153	tdo_tl070wsh30->supply = devm_regulator_get(dev, "power");
154	if (IS_ERR(tdo_tl070wsh30->supply))
155		return PTR_ERR(tdo_tl070wsh30->supply);
156
157	tdo_tl070wsh30->reset_gpio = devm_gpiod_get(dev, "reset",
158						    GPIOD_OUT_LOW);
159	if (IS_ERR(tdo_tl070wsh30->reset_gpio)) {
160		err = PTR_ERR(tdo_tl070wsh30->reset_gpio);
161		dev_dbg(dev, "failed to get reset gpio: %d\n", err);
162		return err;
163	}
164
165	drm_panel_init(&tdo_tl070wsh30->base, &tdo_tl070wsh30->link->dev,
166		       &tdo_tl070wsh30_panel_funcs, DRM_MODE_CONNECTOR_DSI);
167
168	err = drm_panel_of_backlight(&tdo_tl070wsh30->base);
169	if (err)
170		return err;
171
172	drm_panel_add(&tdo_tl070wsh30->base);
173
174	return 0;
175}
176
177static int tdo_tl070wsh30_panel_probe(struct mipi_dsi_device *dsi)
178{
179	struct tdo_tl070wsh30_panel *tdo_tl070wsh30;
180	int err;
181
182	dsi->lanes = 4;
183	dsi->format = MIPI_DSI_FMT_RGB888;
184	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
185
186	tdo_tl070wsh30 = devm_kzalloc(&dsi->dev, sizeof(*tdo_tl070wsh30),
187				    GFP_KERNEL);
188	if (!tdo_tl070wsh30)
189		return -ENOMEM;
190
191	mipi_dsi_set_drvdata(dsi, tdo_tl070wsh30);
192	tdo_tl070wsh30->link = dsi;
193
194	err = tdo_tl070wsh30_panel_add(tdo_tl070wsh30);
195	if (err < 0)
196		return err;
197
198	return mipi_dsi_attach(dsi);
199}
200
201static void tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi)
202{
203	struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
204	int err;
205
206	err = mipi_dsi_detach(dsi);
207	if (err < 0)
208		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
209
210	drm_panel_remove(&tdo_tl070wsh30->base);
211}
212
213static struct mipi_dsi_driver tdo_tl070wsh30_panel_driver = {
214	.driver = {
215		.name = "panel-tdo-tl070wsh30",
216		.of_match_table = tdo_tl070wsh30_of_match,
217	},
218	.probe = tdo_tl070wsh30_panel_probe,
219	.remove = tdo_tl070wsh30_panel_remove,
220};
221module_mipi_dsi_driver(tdo_tl070wsh30_panel_driver);
222
223MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
224MODULE_DESCRIPTION("TDO TL070WSH30 panel driver");
225MODULE_LICENSE("GPL v2");