Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2022 Konrad Dybcio <konrad.dybcio@somainline.org>
  4 *
  5 * Generated with linux-mdss-dsi-panel-driver-generator with a
  6 * substantial amount of manual adjustments.
  7 *
  8 * SONY Downstream kernel calls this one:
  9 * - "JDI ID3" for Akari  (XZ2)
 10 * - "JDI ID4" for Apollo (XZ2 Compact)
 11 */
 12
 13#include <linux/delay.h>
 14#include <linux/gpio/consumer.h>
 15#include <linux/module.h>
 16#include <linux/of.h>
 17#include <linux/regulator/consumer.h>
 18
 19#include <video/mipi_display.h>
 20
 21#include <drm/drm_mipi_dsi.h>
 22#include <drm/drm_modes.h>
 23#include <drm/drm_panel.h>
 24
 25enum {
 26	TYPE_TAMA_60HZ,
 27	/*
 28	 * Leaving room for expansion - SONY very often uses
 29	 * *truly reliably* overclockable panels on their flagships!
 30	 */
 31};
 32
 33struct sony_td4353_jdi {
 34	struct drm_panel panel;
 35	struct mipi_dsi_device *dsi;
 36	struct regulator_bulk_data supplies[3];
 37	struct gpio_desc *panel_reset_gpio;
 38	struct gpio_desc *touch_reset_gpio;
 39	int type;
 40};
 41
 42static inline struct sony_td4353_jdi *to_sony_td4353_jdi(struct drm_panel *panel)
 43{
 44	return container_of(panel, struct sony_td4353_jdi, panel);
 45}
 46
 47static int sony_td4353_jdi_on(struct sony_td4353_jdi *ctx)
 48{
 49	struct mipi_dsi_device *dsi = ctx->dsi;
 50	struct device *dev = &dsi->dev;
 51	int ret;
 52
 53	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
 54
 55	ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 1080 - 1);
 56	if (ret < 0) {
 57		dev_err(dev, "Failed to set column address: %d\n", ret);
 58		return ret;
 59	}
 60
 61	ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 2160 - 1);
 62	if (ret < 0) {
 63		dev_err(dev, "Failed to set page address: %d\n", ret);
 64		return ret;
 65	}
 66
 67	ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0);
 68	if (ret < 0) {
 69		dev_err(dev, "Failed to set tear scanline: %d\n", ret);
 70		return ret;
 71	}
 72
 73	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
 74	if (ret < 0) {
 75		dev_err(dev, "Failed to set tear on: %d\n", ret);
 76		return ret;
 77	}
 78
 79	mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
 80
 81	ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
 82	if (ret < 0) {
 83		dev_err(dev, "Failed to set pixel format: %d\n", ret);
 84		return ret;
 85	}
 86
 87	mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_PARTIAL_ROWS,
 88			  0x00, 0x00, 0x08, 0x6f);
 89
 90	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
 91	if (ret < 0) {
 92		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
 93		return ret;
 94	}
 95	msleep(70);
 96
 97	mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START);
 98
 99	ret = mipi_dsi_dcs_set_display_on(dsi);
100	if (ret < 0) {
101		dev_err(dev, "Failed to turn display on: %d\n", ret);
102		return ret;
103	}
104
105	return 0;
106}
107
108static int sony_td4353_jdi_off(struct sony_td4353_jdi *ctx)
109{
110	struct mipi_dsi_device *dsi = ctx->dsi;
111	struct device *dev = &dsi->dev;
112	int ret;
113
114	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
115
116	ret = mipi_dsi_dcs_set_display_off(dsi);
117	if (ret < 0) {
118		dev_err(dev, "Failed to set display off: %d\n", ret);
119		return ret;
120	}
121	msleep(22);
122
123	ret = mipi_dsi_dcs_set_tear_off(dsi);
124	if (ret < 0) {
125		dev_err(dev, "Failed to set tear off: %d\n", ret);
126		return ret;
127	}
128
129	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
130	if (ret < 0) {
131		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
132		return ret;
133	}
134	msleep(80);
135
136	return 0;
137}
138
139static void sony_td4353_assert_reset_gpios(struct sony_td4353_jdi *ctx, int mode)
140{
141	gpiod_set_value_cansleep(ctx->touch_reset_gpio, mode);
142	gpiod_set_value_cansleep(ctx->panel_reset_gpio, mode);
143	usleep_range(5000, 5100);
144}
145
146static int sony_td4353_jdi_prepare(struct drm_panel *panel)
147{
148	struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
149	struct device *dev = &ctx->dsi->dev;
150	int ret;
151
152	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
153	if (ret < 0) {
154		dev_err(dev, "Failed to enable regulators: %d\n", ret);
155		return ret;
156	}
157
158	msleep(100);
159
160	sony_td4353_assert_reset_gpios(ctx, 1);
161
162	ret = sony_td4353_jdi_on(ctx);
163	if (ret < 0) {
164		dev_err(dev, "Failed to power on panel: %d\n", ret);
165		sony_td4353_assert_reset_gpios(ctx, 0);
166		regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
167		return ret;
168	}
169
170	return 0;
171}
172
173static int sony_td4353_jdi_unprepare(struct drm_panel *panel)
174{
175	struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
176	struct device *dev = &ctx->dsi->dev;
177	int ret;
178
179	ret = sony_td4353_jdi_off(ctx);
180	if (ret < 0)
181		dev_err(dev, "Failed to power off panel: %d\n", ret);
182
183	sony_td4353_assert_reset_gpios(ctx, 0);
184	regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
185
186	return 0;
187}
188
189static const struct drm_display_mode sony_td4353_jdi_mode_tama_60hz = {
190	.clock = (1080 + 4 + 8 + 8) * (2160 + 259 + 8 + 8) * 60 / 1000,
191	.hdisplay = 1080,
192	.hsync_start = 1080 + 4,
193	.hsync_end = 1080 + 4 + 8,
194	.htotal = 1080 + 4 + 8 + 8,
195	.vdisplay = 2160,
196	.vsync_start = 2160 + 259,
197	.vsync_end = 2160 + 259 + 8,
198	.vtotal = 2160 + 259 + 8 + 8,
199	.width_mm = 64,
200	.height_mm = 128,
201};
202
203static int sony_td4353_jdi_get_modes(struct drm_panel *panel,
204				   struct drm_connector *connector)
205{
206	struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
207	struct drm_display_mode *mode = NULL;
208
209	if (ctx->type == TYPE_TAMA_60HZ)
210		mode = drm_mode_duplicate(connector->dev, &sony_td4353_jdi_mode_tama_60hz);
211	else
212		return -EINVAL;
213
214	if (!mode)
215		return -ENOMEM;
216
217	drm_mode_set_name(mode);
218
219	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
220	connector->display_info.width_mm = mode->width_mm;
221	connector->display_info.height_mm = mode->height_mm;
222	drm_mode_probed_add(connector, mode);
223
224	return 1;
225}
226
227static const struct drm_panel_funcs sony_td4353_jdi_panel_funcs = {
228	.prepare = sony_td4353_jdi_prepare,
229	.unprepare = sony_td4353_jdi_unprepare,
230	.get_modes = sony_td4353_jdi_get_modes,
231};
232
233static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi)
234{
235	struct device *dev = &dsi->dev;
236	struct sony_td4353_jdi *ctx;
237	int ret;
238
239	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
240	if (!ctx)
241		return -ENOMEM;
242
243	ctx->type = (uintptr_t)of_device_get_match_data(dev);
244
245	ctx->supplies[0].supply = "vddio";
246	ctx->supplies[1].supply = "vsp";
247	ctx->supplies[2].supply = "vsn";
248	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
249				      ctx->supplies);
250	if (ret < 0)
251		return dev_err_probe(dev, ret, "Failed to get regulators\n");
252
253	ctx->panel_reset_gpio = devm_gpiod_get(dev, "panel-reset", GPIOD_ASIS);
254	if (IS_ERR(ctx->panel_reset_gpio))
255		return dev_err_probe(dev, PTR_ERR(ctx->panel_reset_gpio),
256				     "Failed to get panel-reset-gpios\n");
257
258	ctx->touch_reset_gpio = devm_gpiod_get(dev, "touch-reset", GPIOD_ASIS);
259	if (IS_ERR(ctx->touch_reset_gpio))
260		return dev_err_probe(dev, PTR_ERR(ctx->touch_reset_gpio),
261				     "Failed to get touch-reset-gpios\n");
262
263	ctx->dsi = dsi;
264	mipi_dsi_set_drvdata(dsi, ctx);
265
266	dsi->lanes = 4;
267	dsi->format = MIPI_DSI_FMT_RGB888;
268	dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
269
270	drm_panel_init(&ctx->panel, dev, &sony_td4353_jdi_panel_funcs,
271		       DRM_MODE_CONNECTOR_DSI);
272
273	ret = drm_panel_of_backlight(&ctx->panel);
274	if (ret)
275		return dev_err_probe(dev, ret, "Failed to get backlight\n");
276
277	drm_panel_add(&ctx->panel);
278
279	ret = mipi_dsi_attach(dsi);
280	if (ret < 0) {
281		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
282		drm_panel_remove(&ctx->panel);
283		return ret;
284	}
285
286	return 0;
287}
288
289static void sony_td4353_jdi_remove(struct mipi_dsi_device *dsi)
290{
291	struct sony_td4353_jdi *ctx = mipi_dsi_get_drvdata(dsi);
292	int ret;
293
294	ret = mipi_dsi_detach(dsi);
295	if (ret < 0)
296		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
297
298	drm_panel_remove(&ctx->panel);
299}
300
301static const struct of_device_id sony_td4353_jdi_of_match[] = {
302	{ .compatible = "sony,td4353-jdi-tama", .data = (void *)TYPE_TAMA_60HZ },
303	{ /* sentinel */ }
304};
305MODULE_DEVICE_TABLE(of, sony_td4353_jdi_of_match);
306
307static struct mipi_dsi_driver sony_td4353_jdi_driver = {
308	.probe = sony_td4353_jdi_probe,
309	.remove = sony_td4353_jdi_remove,
310	.driver = {
311		.name = "panel-sony-td4353-jdi",
312		.of_match_table = sony_td4353_jdi_of_match,
313	},
314};
315module_mipi_dsi_driver(sony_td4353_jdi_driver);
316
317MODULE_AUTHOR("Konrad Dybcio <konrad.dybcio@somainline.org>");
318MODULE_DESCRIPTION("DRM panel driver for SONY Xperia XZ2/XZ2c JDI panel");
319MODULE_LICENSE("GPL");