Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3#include <linux/backlight.h>
  4#include <linux/delay.h>
  5#include <linux/gpio/consumer.h>
  6#include <linux/module.h>
  7#include <linux/of.h>
  8#include <linux/regulator/consumer.h>
  9
 10#include <drm/drm_mipi_dsi.h>
 11#include <drm/drm_modes.h>
 12#include <drm/drm_panel.h>
 13
 14struct tm5p5_nt35596 {
 15	struct drm_panel panel;
 16	struct mipi_dsi_device *dsi;
 17	struct regulator_bulk_data supplies[2];
 18	struct gpio_desc *reset_gpio;
 19};
 20
 21static inline struct tm5p5_nt35596 *to_tm5p5_nt35596(struct drm_panel *panel)
 22{
 23	return container_of(panel, struct tm5p5_nt35596, panel);
 24}
 25
 26static void tm5p5_nt35596_reset(struct tm5p5_nt35596 *ctx)
 27{
 28	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
 29	usleep_range(1000, 2000);
 30	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
 31	usleep_range(1000, 2000);
 32	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
 33	usleep_range(15000, 16000);
 34}
 35
 36static void tm5p5_nt35596_on(struct mipi_dsi_multi_context *dsi_ctx)
 37{
 38	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xff, 0x05);
 39	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xfb, 0x01);
 40	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xc5, 0x31);
 41	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xff, 0x04);
 42	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x01, 0x84);
 43	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x05, 0x25);
 44	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x06, 0x01);
 45	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x07, 0x20);
 46	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x08, 0x06);
 47	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x09, 0x08);
 48	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x0a, 0x10);
 49	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x0b, 0x10);
 50	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x0c, 0x10);
 51	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x0d, 0x14);
 52	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x0e, 0x14);
 53	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x0f, 0x14);
 54	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x10, 0x14);
 55	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x11, 0x14);
 56	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x12, 0x14);
 57	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x17, 0xf3);
 58	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x18, 0xc0);
 59	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x19, 0xc0);
 60	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x1a, 0xc0);
 61	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x1b, 0xb3);
 62	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x1c, 0xb3);
 63	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x1d, 0xb3);
 64	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x1e, 0xb3);
 65	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x1f, 0xb3);
 66	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x20, 0xb3);
 67	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xfb, 0x01);
 68	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xff, 0x00);
 69	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xfb, 0x01);
 70	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x35, 0x01);
 71	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xd3, 0x06);
 72	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0xd4, 0x04);
 73	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x5e, 0x0d);
 74	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x11, 0x00);
 75
 76	mipi_dsi_msleep(dsi_ctx, 100);
 77
 78	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x29, 0x00);
 79	mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x53, 0x24);
 80}
 81
 82static void tm5p5_nt35596_off(struct mipi_dsi_multi_context *dsi_ctx)
 83{
 84	mipi_dsi_dcs_set_display_off_multi(dsi_ctx);
 85
 86	mipi_dsi_msleep(dsi_ctx, 60);
 87
 88	mipi_dsi_dcs_enter_sleep_mode_multi(dsi_ctx);
 89
 90	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x4f, 0x01);
 91}
 92
 93static int tm5p5_nt35596_prepare(struct drm_panel *panel)
 94{
 95	struct tm5p5_nt35596 *ctx = to_tm5p5_nt35596(panel);
 96	struct mipi_dsi_multi_context dsi_ctx =	{.dsi = ctx->dsi};
 97
 98	dsi_ctx.accum_err = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 99	if (dsi_ctx.accum_err)
100		return dsi_ctx.accum_err;
101
102	tm5p5_nt35596_reset(ctx);
103
104	tm5p5_nt35596_on(&dsi_ctx);
105
106	if (dsi_ctx.accum_err) {
107		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
108		regulator_bulk_disable(ARRAY_SIZE(ctx->supplies),
109				       ctx->supplies);
110	}
111
112	return dsi_ctx.accum_err;
113}
114
115static int tm5p5_nt35596_unprepare(struct drm_panel *panel)
116{
117	struct tm5p5_nt35596 *ctx = to_tm5p5_nt35596(panel);
118	struct mipi_dsi_multi_context dsi_ctx =	{.dsi = ctx->dsi};
119
120	tm5p5_nt35596_off(&dsi_ctx);
121
122	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
123	regulator_bulk_disable(ARRAY_SIZE(ctx->supplies),
124			       ctx->supplies);
125
126	return dsi_ctx.accum_err;
127}
128
129static const struct drm_display_mode tm5p5_nt35596_mode = {
130	.clock = (1080 + 100 + 8 + 16) * (1920 + 4 + 2 + 4) * 60 / 1000,
131	.hdisplay = 1080,
132	.hsync_start = 1080 + 100,
133	.hsync_end = 1080 + 100 + 8,
134	.htotal = 1080 + 100 + 8 + 16,
135	.vdisplay = 1920,
136	.vsync_start = 1920 + 4,
137	.vsync_end = 1920 + 4 + 2,
138	.vtotal = 1920 + 4 + 2 + 4,
139	.width_mm = 68,
140	.height_mm = 121,
141};
142
143static int tm5p5_nt35596_get_modes(struct drm_panel *panel,
144				   struct drm_connector *connector)
145{
146	struct drm_display_mode *mode;
147
148	mode = drm_mode_duplicate(connector->dev, &tm5p5_nt35596_mode);
149	if (!mode)
150		return -ENOMEM;
151
152	drm_mode_set_name(mode);
153
154	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
155	connector->display_info.width_mm = mode->width_mm;
156	connector->display_info.height_mm = mode->height_mm;
157	drm_mode_probed_add(connector, mode);
158
159	return 1;
160}
161
162static const struct drm_panel_funcs tm5p5_nt35596_panel_funcs = {
163	.prepare = tm5p5_nt35596_prepare,
164	.unprepare = tm5p5_nt35596_unprepare,
165	.get_modes = tm5p5_nt35596_get_modes,
166};
167
168static int tm5p5_nt35596_bl_update_status(struct backlight_device *bl)
169{
170	struct mipi_dsi_device *dsi = bl_get_data(bl);
171	u16 brightness = backlight_get_brightness(bl);
172	int ret;
173
174	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
175
176	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
177	if (ret < 0)
178		return ret;
179
180	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
181
182	return 0;
183}
184
185static int tm5p5_nt35596_bl_get_brightness(struct backlight_device *bl)
186{
187	struct mipi_dsi_device *dsi = bl_get_data(bl);
188	u16 brightness = bl->props.brightness;
189	int ret;
190
191	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
192
193	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
194	if (ret < 0)
195		return ret;
196
197	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
198
199	return brightness & 0xff;
200}
201
202static const struct backlight_ops tm5p5_nt35596_bl_ops = {
203	.update_status = tm5p5_nt35596_bl_update_status,
204	.get_brightness = tm5p5_nt35596_bl_get_brightness,
205};
206
207static struct backlight_device *
208tm5p5_nt35596_create_backlight(struct mipi_dsi_device *dsi)
209{
210	struct device *dev = &dsi->dev;
211	const struct backlight_properties props = {
212		.type = BACKLIGHT_RAW,
213		.brightness = 255,
214		.max_brightness = 255,
215	};
216
217	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
218					      &tm5p5_nt35596_bl_ops, &props);
219}
220
221static int tm5p5_nt35596_probe(struct mipi_dsi_device *dsi)
222{
223	struct device *dev = &dsi->dev;
224	struct tm5p5_nt35596 *ctx;
225	int ret;
226
227	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
228	if (!ctx)
229		return -ENOMEM;
230
231	ctx->supplies[0].supply = "vdd";
232	ctx->supplies[1].supply = "vddio";
233	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
234				      ctx->supplies);
235	if (ret < 0) {
236		dev_err(dev, "Failed to get regulators: %d\n", ret);
237		return ret;
238	}
239
240	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
241	if (IS_ERR(ctx->reset_gpio)) {
242		ret = PTR_ERR(ctx->reset_gpio);
243		dev_err(dev, "Failed to get reset-gpios: %d\n", ret);
244		return ret;
245	}
246
247	ctx->dsi = dsi;
248	mipi_dsi_set_drvdata(dsi, ctx);
249
250	dsi->lanes = 4;
251	dsi->format = MIPI_DSI_FMT_RGB888;
252	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
253			  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_NO_EOT_PACKET |
254			  MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
255
256	drm_panel_init(&ctx->panel, dev, &tm5p5_nt35596_panel_funcs,
257		       DRM_MODE_CONNECTOR_DSI);
258
259	ctx->panel.backlight = tm5p5_nt35596_create_backlight(dsi);
260	if (IS_ERR(ctx->panel.backlight)) {
261		ret = PTR_ERR(ctx->panel.backlight);
262		dev_err(dev, "Failed to create backlight: %d\n", ret);
263		return ret;
264	}
265
266	drm_panel_add(&ctx->panel);
267
268	ret = mipi_dsi_attach(dsi);
269	if (ret < 0) {
270		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
271		return ret;
272	}
273
274	return 0;
275}
276
277static void tm5p5_nt35596_remove(struct mipi_dsi_device *dsi)
278{
279	struct tm5p5_nt35596 *ctx = mipi_dsi_get_drvdata(dsi);
280	int ret;
281
282	ret = mipi_dsi_detach(dsi);
283	if (ret < 0)
284		dev_err(&dsi->dev,
285			"Failed to detach from DSI host: %d\n", ret);
286
287	drm_panel_remove(&ctx->panel);
288}
289
290static const struct of_device_id tm5p5_nt35596_of_match[] = {
291	{ .compatible = "asus,z00t-tm5p5-n35596" },
292	{ /* sentinel */ }
293};
294MODULE_DEVICE_TABLE(of, tm5p5_nt35596_of_match);
295
296static struct mipi_dsi_driver tm5p5_nt35596_driver = {
297	.probe = tm5p5_nt35596_probe,
298	.remove = tm5p5_nt35596_remove,
299	.driver = {
300		.name = "panel-tm5p5-nt35596",
301		.of_match_table = tm5p5_nt35596_of_match,
302	},
303};
304module_mipi_dsi_driver(tm5p5_nt35596_driver);
305
306MODULE_AUTHOR("Konrad Dybcio <konradybcio@gmail.com>");
307MODULE_DESCRIPTION("DRM driver for tm5p5 nt35596 1080p video mode dsi panel");
308MODULE_LICENSE("GPL v2");