Linux Audio

Check our new training course

Loading...
v6.13.7
  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");
v6.2
  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	bool prepared;
 29};
 30
 31static inline
 32struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel)
 33{
 34	return container_of(panel, struct tdo_tl070wsh30_panel, base);
 35}
 36
 37static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
 38{
 39	struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
 40	int err;
 41
 42	if (tdo_tl070wsh30->prepared)
 43		return 0;
 44
 45	err = regulator_enable(tdo_tl070wsh30->supply);
 46	if (err < 0)
 47		return err;
 48
 49	usleep_range(10000, 11000);
 50
 51	gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 1);
 52
 53	usleep_range(10000, 11000);
 54
 55	gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 0);
 56
 57	msleep(200);
 58
 59	err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link);
 60	if (err < 0) {
 61		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
 62		regulator_disable(tdo_tl070wsh30->supply);
 63		return err;
 64	}
 65
 66	msleep(200);
 67
 68	err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link);
 69	if (err < 0) {
 70		dev_err(panel->dev, "failed to set display on: %d\n", err);
 71		regulator_disable(tdo_tl070wsh30->supply);
 72		return err;
 73	}
 74
 75	msleep(20);
 76
 77	tdo_tl070wsh30->prepared = true;
 78
 79	return 0;
 80}
 81
 82static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel)
 83{
 84	struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
 85	int err;
 86
 87	if (!tdo_tl070wsh30->prepared)
 88		return 0;
 89
 90	err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link);
 91	if (err < 0)
 92		dev_err(panel->dev, "failed to set display off: %d\n", err);
 93
 94	usleep_range(10000, 11000);
 95
 96	err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link);
 97	if (err < 0) {
 98		dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
 99		return err;
100	}
101
102	usleep_range(10000, 11000);
103
104	regulator_disable(tdo_tl070wsh30->supply);
105
106	tdo_tl070wsh30->prepared = false;
107
108	return 0;
109}
110
111static const struct drm_display_mode default_mode = {
112	.clock = 47250,
113	.hdisplay = 1024,
114	.hsync_start = 1024 + 46,
115	.hsync_end = 1024 + 46 + 80,
116	.htotal = 1024 + 46 + 80 + 100,
117	.vdisplay = 600,
118	.vsync_start = 600 + 5,
119	.vsync_end = 600 + 5 + 5,
120	.vtotal = 600 + 5 + 5 + 20,
121	.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
122};
123
124static int tdo_tl070wsh30_panel_get_modes(struct drm_panel *panel,
125				       struct drm_connector *connector)
126{
127	struct drm_display_mode *mode;
128
129	mode = drm_mode_duplicate(connector->dev, &default_mode);
130	if (!mode) {
131		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
132			default_mode.hdisplay, default_mode.vdisplay,
133			drm_mode_vrefresh(&default_mode));
134		return -ENOMEM;
135	}
136
137	drm_mode_set_name(mode);
138
139	drm_mode_probed_add(connector, mode);
140
141	connector->display_info.width_mm = 154;
142	connector->display_info.height_mm = 85;
143	connector->display_info.bpc = 8;
144
145	return 1;
146}
147
148static const struct drm_panel_funcs tdo_tl070wsh30_panel_funcs = {
149	.unprepare = tdo_tl070wsh30_panel_unprepare,
150	.prepare = tdo_tl070wsh30_panel_prepare,
151	.get_modes = tdo_tl070wsh30_panel_get_modes,
152};
153
154static const struct of_device_id tdo_tl070wsh30_of_match[] = {
155	{ .compatible = "tdo,tl070wsh30", },
156	{ /* sentinel */ }
157};
158MODULE_DEVICE_TABLE(of, tdo_tl070wsh30_of_match);
159
160static int tdo_tl070wsh30_panel_add(struct tdo_tl070wsh30_panel *tdo_tl070wsh30)
161{
162	struct device *dev = &tdo_tl070wsh30->link->dev;
163	int err;
164
165	tdo_tl070wsh30->supply = devm_regulator_get(dev, "power");
166	if (IS_ERR(tdo_tl070wsh30->supply))
167		return PTR_ERR(tdo_tl070wsh30->supply);
168
169	tdo_tl070wsh30->reset_gpio = devm_gpiod_get(dev, "reset",
170						    GPIOD_OUT_LOW);
171	if (IS_ERR(tdo_tl070wsh30->reset_gpio)) {
172		err = PTR_ERR(tdo_tl070wsh30->reset_gpio);
173		dev_dbg(dev, "failed to get reset gpio: %d\n", err);
174		return err;
175	}
176
177	drm_panel_init(&tdo_tl070wsh30->base, &tdo_tl070wsh30->link->dev,
178		       &tdo_tl070wsh30_panel_funcs, DRM_MODE_CONNECTOR_DSI);
179
180	err = drm_panel_of_backlight(&tdo_tl070wsh30->base);
181	if (err)
182		return err;
183
184	drm_panel_add(&tdo_tl070wsh30->base);
185
186	return 0;
187}
188
189static int tdo_tl070wsh30_panel_probe(struct mipi_dsi_device *dsi)
190{
191	struct tdo_tl070wsh30_panel *tdo_tl070wsh30;
192	int err;
193
194	dsi->lanes = 4;
195	dsi->format = MIPI_DSI_FMT_RGB888;
196	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
197
198	tdo_tl070wsh30 = devm_kzalloc(&dsi->dev, sizeof(*tdo_tl070wsh30),
199				    GFP_KERNEL);
200	if (!tdo_tl070wsh30)
201		return -ENOMEM;
202
203	mipi_dsi_set_drvdata(dsi, tdo_tl070wsh30);
204	tdo_tl070wsh30->link = dsi;
205
206	err = tdo_tl070wsh30_panel_add(tdo_tl070wsh30);
207	if (err < 0)
208		return err;
209
210	return mipi_dsi_attach(dsi);
211}
212
213static void tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi)
214{
215	struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
216	int err;
217
218	err = mipi_dsi_detach(dsi);
219	if (err < 0)
220		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
221
222	drm_panel_remove(&tdo_tl070wsh30->base);
223	drm_panel_disable(&tdo_tl070wsh30->base);
224	drm_panel_unprepare(&tdo_tl070wsh30->base);
225}
226
227static void tdo_tl070wsh30_panel_shutdown(struct mipi_dsi_device *dsi)
228{
229	struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
230
231	drm_panel_disable(&tdo_tl070wsh30->base);
232	drm_panel_unprepare(&tdo_tl070wsh30->base);
233}
234
235static struct mipi_dsi_driver tdo_tl070wsh30_panel_driver = {
236	.driver = {
237		.name = "panel-tdo-tl070wsh30",
238		.of_match_table = tdo_tl070wsh30_of_match,
239	},
240	.probe = tdo_tl070wsh30_panel_probe,
241	.remove = tdo_tl070wsh30_panel_remove,
242	.shutdown = tdo_tl070wsh30_panel_shutdown,
243};
244module_mipi_dsi_driver(tdo_tl070wsh30_panel_driver);
245
246MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
247MODULE_DESCRIPTION("TDO TL070WSH30 panel driver");
248MODULE_LICENSE("GPL v2");