Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
  4 *
  5 * Copyright (C) Purism SPC 2019
  6 */
  7
  8#include <drm/drm_mipi_dsi.h>
  9#include <drm/drm_modes.h>
 10#include <drm/drm_panel.h>
 11#include <drm/drm_print.h>
 12#include <linux/backlight.h>
 13#include <linux/debugfs.h>
 14#include <linux/delay.h>
 15#include <linux/gpio/consumer.h>
 16#include <linux/media-bus-format.h>
 17#include <linux/module.h>
 18#include <linux/regulator/consumer.h>
 19#include <video/display_timing.h>
 20#include <video/mipi_display.h>
 21
 22#define DRV_NAME "panel-rocktech-jh057n00900"
 23
 24/* Manufacturer specific Commands send via DSI */
 25#define ST7703_CMD_ALL_PIXEL_OFF 0x22
 26#define ST7703_CMD_ALL_PIXEL_ON	 0x23
 27#define ST7703_CMD_SETDISP	 0xB2
 28#define ST7703_CMD_SETRGBIF	 0xB3
 29#define ST7703_CMD_SETCYC	 0xB4
 30#define ST7703_CMD_SETBGP	 0xB5
 31#define ST7703_CMD_SETVCOM	 0xB6
 32#define ST7703_CMD_SETOTP	 0xB7
 33#define ST7703_CMD_SETPOWER_EXT	 0xB8
 34#define ST7703_CMD_SETEXTC	 0xB9
 35#define ST7703_CMD_SETMIPI	 0xBA
 36#define ST7703_CMD_SETVDC	 0xBC
 37#define ST7703_CMD_UNKNOWN0	 0xBF
 38#define ST7703_CMD_SETSCR	 0xC0
 39#define ST7703_CMD_SETPOWER	 0xC1
 40#define ST7703_CMD_SETPANEL	 0xCC
 41#define ST7703_CMD_SETGAMMA	 0xE0
 42#define ST7703_CMD_SETEQ	 0xE3
 43#define ST7703_CMD_SETGIP1	 0xE9
 44#define ST7703_CMD_SETGIP2	 0xEA
 45
 46struct jh057n {
 47	struct device *dev;
 48	struct drm_panel panel;
 49	struct gpio_desc *reset_gpio;
 50	struct backlight_device *backlight;
 51	struct regulator *vcc;
 52	struct regulator *iovcc;
 53	bool prepared;
 54
 55	struct dentry *debugfs;
 56};
 57
 58static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel)
 59{
 60	return container_of(panel, struct jh057n, panel);
 61}
 62
 63#define dsi_generic_write_seq(dsi, seq...) do {				\
 64		static const u8 d[] = { seq };				\
 65		int ret;						\
 66		ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d));	\
 67		if (ret < 0)						\
 68			return ret;					\
 69	} while (0)
 70
 71static int jh057n_init_sequence(struct jh057n *ctx)
 72{
 73	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 74	struct device *dev = ctx->dev;
 75	int ret;
 76
 77	/*
 78	 * Init sequence was supplied by the panel vendor. Most of the commands
 79	 * resemble the ST7703 but the number of parameters often don't match
 80	 * so it's likely a clone.
 81	 */
 82	dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC,
 83			      0xF1, 0x12, 0x83);
 84	dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF,
 85			      0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
 86			      0x00, 0x00);
 87	dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR,
 88			      0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
 89			      0x00);
 90	dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E);
 91	dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);
 92	dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
 93	dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30);
 94	dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ,
 95			      0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
 96			      0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
 97	dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08);
 98	msleep(20);
 99
100	dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F);
101	dsi_generic_write_seq(dsi, ST7703_CMD_UNKNOWN0, 0x02, 0x11, 0x00);
102	dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1,
103			      0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12,
104			      0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
105			      0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
106			      0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
107			      0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
108			      0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
109			      0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110			      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
111	dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2,
112			      0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113			      0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
114			      0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
115			      0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
116			      0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
117			      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118			      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A,
119			      0xA5, 0x00, 0x00, 0x00, 0x00);
120	dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA,
121			      0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37,
122			      0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11,
123			      0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41,
124			      0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10,
125			      0x11, 0x18);
126	msleep(20);
127
128	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
129	if (ret < 0) {
130		DRM_DEV_ERROR(dev, "Failed to exit sleep mode: %d\n", ret);
131		return ret;
132	}
133	/* Panel is operational 120 msec after reset */
134	msleep(60);
135	ret = mipi_dsi_dcs_set_display_on(dsi);
136	if (ret)
137		return ret;
138
139	DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n");
140	return 0;
141}
142
143static int jh057n_enable(struct drm_panel *panel)
144{
145	struct jh057n *ctx = panel_to_jh057n(panel);
146	int ret;
147
148	ret = jh057n_init_sequence(ctx);
149	if (ret < 0) {
150		DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
151			      ret);
152		return ret;
153	}
154
155	return backlight_enable(ctx->backlight);
156}
157
158static int jh057n_disable(struct drm_panel *panel)
159{
160	struct jh057n *ctx = panel_to_jh057n(panel);
161	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
162
163	backlight_disable(ctx->backlight);
164	return mipi_dsi_dcs_set_display_off(dsi);
165}
166
167static int jh057n_unprepare(struct drm_panel *panel)
168{
169	struct jh057n *ctx = panel_to_jh057n(panel);
170
171	if (!ctx->prepared)
172		return 0;
173
174	regulator_disable(ctx->iovcc);
175	regulator_disable(ctx->vcc);
176	ctx->prepared = false;
177
178	return 0;
179}
180
181static int jh057n_prepare(struct drm_panel *panel)
182{
183	struct jh057n *ctx = panel_to_jh057n(panel);
184	int ret;
185
186	if (ctx->prepared)
187		return 0;
188
189	DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
190	ret = regulator_enable(ctx->vcc);
191	if (ret < 0) {
192		DRM_DEV_ERROR(ctx->dev,
193			      "Failed to enable vcc supply: %d\n", ret);
194		return ret;
195	}
196	ret = regulator_enable(ctx->iovcc);
197	if (ret < 0) {
198		DRM_DEV_ERROR(ctx->dev,
199			      "Failed to enable iovcc supply: %d\n", ret);
200		goto disable_vcc;
201	}
202
203	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
204	usleep_range(20, 40);
205	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
206	msleep(20);
207
208	ctx->prepared = true;
209
210	return 0;
211
212disable_vcc:
213	regulator_disable(ctx->vcc);
214	return ret;
215}
216
217static const struct drm_display_mode default_mode = {
218	.hdisplay    = 720,
219	.hsync_start = 720 + 90,
220	.hsync_end   = 720 + 90 + 20,
221	.htotal	     = 720 + 90 + 20 + 20,
222	.vdisplay    = 1440,
223	.vsync_start = 1440 + 20,
224	.vsync_end   = 1440 + 20 + 4,
225	.vtotal	     = 1440 + 20 + 4 + 12,
226	.vrefresh    = 60,
227	.clock	     = 75276,
228	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
229	.width_mm    = 65,
230	.height_mm   = 130,
231};
232
233static int jh057n_get_modes(struct drm_panel *panel)
234{
235	struct jh057n *ctx = panel_to_jh057n(panel);
236	struct drm_display_mode *mode;
237
238	mode = drm_mode_duplicate(panel->drm, &default_mode);
239	if (!mode) {
240		DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n",
241			      default_mode.hdisplay, default_mode.vdisplay,
242			      default_mode.vrefresh);
243		return -ENOMEM;
244	}
245
246	drm_mode_set_name(mode);
247
248	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
249	panel->connector->display_info.width_mm = mode->width_mm;
250	panel->connector->display_info.height_mm = mode->height_mm;
251	drm_mode_probed_add(panel->connector, mode);
252
253	return 1;
254}
255
256static const struct drm_panel_funcs jh057n_drm_funcs = {
257	.disable   = jh057n_disable,
258	.unprepare = jh057n_unprepare,
259	.prepare   = jh057n_prepare,
260	.enable	   = jh057n_enable,
261	.get_modes = jh057n_get_modes,
262};
263
264static int allpixelson_set(void *data, u64 val)
265{
266	struct jh057n *ctx = data;
267	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
268
269	DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n");
270	dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON);
271	msleep(val * 1000);
272	/* Reset the panel to get video back */
273	drm_panel_disable(&ctx->panel);
274	drm_panel_unprepare(&ctx->panel);
275	drm_panel_prepare(&ctx->panel);
276	drm_panel_enable(&ctx->panel);
277
278	return 0;
279}
280
281DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL,
282			allpixelson_set, "%llu\n");
283
284static void jh057n_debugfs_init(struct jh057n *ctx)
285{
286	ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL);
287
288	debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx,
289			    &allpixelson_fops);
290}
291
292static void jh057n_debugfs_remove(struct jh057n *ctx)
293{
294	debugfs_remove_recursive(ctx->debugfs);
295	ctx->debugfs = NULL;
296}
297
298static int jh057n_probe(struct mipi_dsi_device *dsi)
299{
300	struct device *dev = &dsi->dev;
301	struct jh057n *ctx;
302	int ret;
303
304	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
305	if (!ctx)
306		return -ENOMEM;
307
308	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
309	if (IS_ERR(ctx->reset_gpio)) {
310		DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
311		return PTR_ERR(ctx->reset_gpio);
312	}
313
314	mipi_dsi_set_drvdata(dsi, ctx);
315
316	ctx->dev = dev;
317
318	dsi->lanes = 4;
319	dsi->format = MIPI_DSI_FMT_RGB888;
320	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
321		MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
322
323	ctx->backlight = devm_of_find_backlight(dev);
324	if (IS_ERR(ctx->backlight))
325		return PTR_ERR(ctx->backlight);
326
327	ctx->vcc = devm_regulator_get(dev, "vcc");
328	if (IS_ERR(ctx->vcc)) {
329		ret = PTR_ERR(ctx->vcc);
330		if (ret != -EPROBE_DEFER)
331			DRM_DEV_ERROR(dev,
332				      "Failed to request vcc regulator: %d\n",
333				      ret);
334		return ret;
335	}
336	ctx->iovcc = devm_regulator_get(dev, "iovcc");
337	if (IS_ERR(ctx->iovcc)) {
338		ret = PTR_ERR(ctx->iovcc);
339		if (ret != -EPROBE_DEFER)
340			DRM_DEV_ERROR(dev,
341				      "Failed to request iovcc regulator: %d\n",
342				      ret);
343		return ret;
344	}
345
346	drm_panel_init(&ctx->panel);
347	ctx->panel.dev = dev;
348	ctx->panel.funcs = &jh057n_drm_funcs;
349
350	drm_panel_add(&ctx->panel);
351
352	ret = mipi_dsi_attach(dsi);
353	if (ret < 0) {
354		DRM_DEV_ERROR(dev,
355			      "mipi_dsi_attach failed (%d). Is host ready?\n",
356			      ret);
357		drm_panel_remove(&ctx->panel);
358		return ret;
359	}
360
361	DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
362		     default_mode.hdisplay, default_mode.vdisplay,
363		     default_mode.vrefresh,
364		     mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
365
366	jh057n_debugfs_init(ctx);
367	return 0;
368}
369
370static void jh057n_shutdown(struct mipi_dsi_device *dsi)
371{
372	struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
373	int ret;
374
375	ret = drm_panel_unprepare(&ctx->panel);
376	if (ret < 0)
377		DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
378			      ret);
379
380	ret = drm_panel_disable(&ctx->panel);
381	if (ret < 0)
382		DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
383			      ret);
384}
385
386static int jh057n_remove(struct mipi_dsi_device *dsi)
387{
388	struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
389	int ret;
390
391	jh057n_shutdown(dsi);
392
393	ret = mipi_dsi_detach(dsi);
394	if (ret < 0)
395		DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
396			      ret);
397
398	drm_panel_remove(&ctx->panel);
399
400	jh057n_debugfs_remove(ctx);
401
402	return 0;
403}
404
405static const struct of_device_id jh057n_of_match[] = {
406	{ .compatible = "rocktech,jh057n00900" },
407	{ /* sentinel */ }
408};
409MODULE_DEVICE_TABLE(of, jh057n_of_match);
410
411static struct mipi_dsi_driver jh057n_driver = {
412	.probe	= jh057n_probe,
413	.remove = jh057n_remove,
414	.shutdown = jh057n_shutdown,
415	.driver = {
416		.name = DRV_NAME,
417		.of_match_table = jh057n_of_match,
418	},
419};
420module_mipi_dsi_driver(jh057n_driver);
421
422MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
423MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel");
424MODULE_LICENSE("GPL v2");