cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

panel-novatek-nt35950.c (17567B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Novatek NT35950 DriverIC panels driver
      4 *
      5 * Copyright (c) 2021 AngeloGioacchino Del Regno
      6 *                    <angelogioacchino.delregno@somainline.org>
      7 */
      8#include <linux/delay.h>
      9#include <linux/gpio/consumer.h>
     10#include <linux/module.h>
     11#include <linux/of_device.h>
     12#include <linux/of_graph.h>
     13#include <linux/regulator/consumer.h>
     14
     15#include <drm/drm_connector.h>
     16#include <drm/drm_crtc.h>
     17#include <drm/drm_mipi_dsi.h>
     18#include <drm/drm_modes.h>
     19#include <drm/drm_panel.h>
     20
     21#define MCS_CMD_MAUCCTR			0xf0 /* Manufacturer command enable */
     22#define MCS_PARAM_SCALER_FUNCTION	0x58 /* Scale-up function */
     23#define MCS_PARAM_SCALEUP_MODE		0xc9
     24 #define MCS_SCALEUP_SIMPLE		0x0
     25 #define MCS_SCALEUP_BILINEAR		BIT(0)
     26 #define MCS_SCALEUP_DUPLICATE		(BIT(0) | BIT(4))
     27
     28/* VESA Display Stream Compression param */
     29#define MCS_PARAM_VESA_DSC_ON		0x03
     30
     31/* Data Compression mode */
     32#define MCS_PARAM_DATA_COMPRESSION	0x90
     33 #define MCS_DATA_COMPRESSION_NONE	0x00
     34 #define MCS_DATA_COMPRESSION_FBC	0x02
     35 #define MCS_DATA_COMPRESSION_DSC	0x03
     36
     37/* Display Output control */
     38#define MCS_PARAM_DISP_OUTPUT_CTRL	0xb4
     39 #define MCS_DISP_OUT_SRAM_EN		BIT(0)
     40 #define MCS_DISP_OUT_VIDEO_MODE	BIT(4)
     41
     42/* VESA Display Stream Compression setting */
     43#define MCS_PARAM_VESA_DSC_SETTING	0xc0
     44
     45/* SubPixel Rendering (SPR) */
     46#define MCS_PARAM_SPR_EN		0xe3
     47#define MCS_PARAM_SPR_MODE		0xef
     48 #define MCS_SPR_MODE_YYG_RAINBOW_RGB	0x01
     49
     50#define NT35950_VREG_MAX		4
     51
     52struct nt35950 {
     53	struct drm_panel panel;
     54	struct drm_connector *connector;
     55	struct mipi_dsi_device *dsi[2];
     56	struct regulator_bulk_data vregs[NT35950_VREG_MAX];
     57	struct gpio_desc *reset_gpio;
     58	const struct nt35950_panel_desc *desc;
     59
     60	int cur_mode;
     61	u8 last_page;
     62	bool prepared;
     63};
     64
     65struct nt35950_panel_mode {
     66	const struct drm_display_mode mode;
     67
     68	bool enable_sram;
     69	bool is_video_mode;
     70	u8 scaler_on;
     71	u8 scaler_mode;
     72	u8 compression;
     73	u8 spr_en;
     74	u8 spr_mode;
     75};
     76
     77struct nt35950_panel_desc {
     78	const char *model_name;
     79	const struct mipi_dsi_device_info dsi_info;
     80	const struct nt35950_panel_mode *mode_data;
     81
     82	bool is_dual_dsi;
     83	u8 num_lanes;
     84	u8 num_modes;
     85};
     86
     87static inline struct nt35950 *to_nt35950(struct drm_panel *panel)
     88{
     89	return container_of(panel, struct nt35950, panel);
     90}
     91
     92#define dsi_dcs_write_seq(dsi, seq...) do {				\
     93		static const u8 d[] = { seq };				\
     94		int ret;						\
     95		ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d));	\
     96		if (ret < 0)						\
     97			return ret;					\
     98	} while (0)
     99
    100static void nt35950_reset(struct nt35950 *nt)
    101{
    102	gpiod_set_value_cansleep(nt->reset_gpio, 1);
    103	usleep_range(12000, 13000);
    104	gpiod_set_value_cansleep(nt->reset_gpio, 0);
    105	usleep_range(300, 400);
    106	gpiod_set_value_cansleep(nt->reset_gpio, 1);
    107	usleep_range(12000, 13000);
    108}
    109
    110/*
    111 * nt35950_set_cmd2_page - Select manufacturer control (CMD2) page
    112 * @nt:   Main driver structure
    113 * @page: Page number (0-7)
    114 *
    115 * Return: Number of transferred bytes or negative number on error
    116 */
    117static int nt35950_set_cmd2_page(struct nt35950 *nt, u8 page)
    118{
    119	const u8 mauc_cmd2_page[] = { MCS_CMD_MAUCCTR, 0x55, 0xaa, 0x52,
    120				      0x08, page };
    121	int ret;
    122
    123	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], mauc_cmd2_page,
    124					ARRAY_SIZE(mauc_cmd2_page));
    125	if (ret < 0)
    126		return ret;
    127
    128	nt->last_page = page;
    129	return 0;
    130}
    131
    132/*
    133 * nt35950_set_data_compression - Set data compression mode
    134 * @nt:        Main driver structure
    135 * @comp_mode: Compression mode
    136 *
    137 * Return: Number of transferred bytes or negative number on error
    138 */
    139static int nt35950_set_data_compression(struct nt35950 *nt, u8 comp_mode)
    140{
    141	u8 cmd_data_compression[] = { MCS_PARAM_DATA_COMPRESSION, comp_mode };
    142	u8 cmd_vesa_dsc_on[] = { MCS_PARAM_VESA_DSC_ON, !!comp_mode };
    143	u8 cmd_vesa_dsc_setting[] = { MCS_PARAM_VESA_DSC_SETTING, 0x03 };
    144	u8 last_page = nt->last_page;
    145	int ret;
    146
    147	/* Set CMD2 Page 0 if we're not there yet */
    148	if (last_page != 0) {
    149		ret = nt35950_set_cmd2_page(nt, 0);
    150		if (ret < 0)
    151			return ret;
    152	}
    153
    154	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_data_compression,
    155					ARRAY_SIZE(cmd_data_compression));
    156	if (ret < 0)
    157		return ret;
    158
    159	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_on,
    160					ARRAY_SIZE(cmd_vesa_dsc_on));
    161	if (ret < 0)
    162		return ret;
    163
    164	/* Set the vesa dsc setting on Page 4 */
    165	ret = nt35950_set_cmd2_page(nt, 4);
    166	if (ret < 0)
    167		return ret;
    168
    169	/* Display Stream Compression setting, always 0x03 */
    170	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_setting,
    171					ARRAY_SIZE(cmd_vesa_dsc_setting));
    172	if (ret < 0)
    173		return ret;
    174
    175	/* Get back to the previously set page */
    176	return nt35950_set_cmd2_page(nt, last_page);
    177}
    178
    179/*
    180 * nt35950_set_scaler - Enable/disable resolution upscaling
    181 * @nt:        Main driver structure
    182 * @scale_up:  Scale up function control
    183 *
    184 * Return: Number of transferred bytes or negative number on error
    185 */
    186static int nt35950_set_scaler(struct nt35950 *nt, u8 scale_up)
    187{
    188	u8 cmd_scaler[] = { MCS_PARAM_SCALER_FUNCTION, scale_up };
    189
    190	return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler,
    191					 ARRAY_SIZE(cmd_scaler));
    192}
    193
    194/*
    195 * nt35950_set_scale_mode - Resolution upscaling mode
    196 * @nt:   Main driver structure
    197 * @mode: Scaler mode (MCS_DATA_COMPRESSION_*)
    198 *
    199 * Return: Number of transferred bytes or negative number on error
    200 */
    201static int nt35950_set_scale_mode(struct nt35950 *nt, u8 mode)
    202{
    203	u8 cmd_scaler[] = { MCS_PARAM_SCALEUP_MODE, mode };
    204
    205	return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler,
    206					 ARRAY_SIZE(cmd_scaler));
    207}
    208
    209/*
    210 * nt35950_inject_black_image - Display a completely black image
    211 * @nt:   Main driver structure
    212 *
    213 * After IC setup, the attached panel may show random data
    214 * due to driveric behavior changes (resolution, compression,
    215 * scaling, etc). This function, called after parameters setup,
    216 * makes the driver ic to output a completely black image to
    217 * the display.
    218 * It makes sense to push a black image before sending the sleep-out
    219 * and display-on commands.
    220 *
    221 * Return: Number of transferred bytes or negative number on error
    222 */
    223static int nt35950_inject_black_image(struct nt35950 *nt)
    224{
    225	const u8 cmd0_black_img[] = { 0x6f, 0x01 };
    226	const u8 cmd1_black_img[] = { 0xf3, 0x10 };
    227	u8 cmd_test[] = { 0xff, 0xaa, 0x55, 0xa5, 0x80 };
    228	int ret;
    229
    230	/* Enable test command */
    231	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test));
    232	if (ret < 0)
    233		return ret;
    234
    235	/* Send a black image */
    236	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd0_black_img,
    237					ARRAY_SIZE(cmd0_black_img));
    238	if (ret < 0)
    239		return ret;
    240	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd1_black_img,
    241					ARRAY_SIZE(cmd1_black_img));
    242	if (ret < 0)
    243		return ret;
    244
    245	/* Disable test command */
    246	cmd_test[ARRAY_SIZE(cmd_test) - 1] = 0x00;
    247	return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test));
    248}
    249
    250/*
    251 * nt35950_set_dispout - Set Display Output register parameters
    252 * @nt:    Main driver structure
    253 *
    254 * Return: Number of transferred bytes or negative number on error
    255 */
    256static int nt35950_set_dispout(struct nt35950 *nt)
    257{
    258	u8 cmd_dispout[] = { MCS_PARAM_DISP_OUTPUT_CTRL, 0x00 };
    259	const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;
    260
    261	if (mode_data[nt->cur_mode].is_video_mode)
    262		cmd_dispout[1] |= MCS_DISP_OUT_VIDEO_MODE;
    263	if (mode_data[nt->cur_mode].enable_sram)
    264		cmd_dispout[1] |= MCS_DISP_OUT_SRAM_EN;
    265
    266	return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_dispout,
    267					 ARRAY_SIZE(cmd_dispout));
    268}
    269
    270static int nt35950_get_current_mode(struct nt35950 *nt)
    271{
    272	struct drm_connector *connector = nt->connector;
    273	struct drm_crtc_state *crtc_state;
    274	int i;
    275
    276	/* Return the default (first) mode if no info available yet */
    277	if (!connector->state || !connector->state->crtc)
    278		return 0;
    279
    280	crtc_state = connector->state->crtc->state;
    281
    282	for (i = 0; i < nt->desc->num_modes; i++) {
    283		if (drm_mode_match(&crtc_state->mode,
    284				   &nt->desc->mode_data[i].mode,
    285				   DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK))
    286			return i;
    287	}
    288
    289	return 0;
    290}
    291
    292static int nt35950_on(struct nt35950 *nt)
    293{
    294	const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;
    295	struct mipi_dsi_device *dsi = nt->dsi[0];
    296	struct device *dev = &dsi->dev;
    297	int ret;
    298
    299	nt->cur_mode = nt35950_get_current_mode(nt);
    300	nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
    301	nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
    302
    303	ret = nt35950_set_cmd2_page(nt, 0);
    304	if (ret < 0)
    305		return ret;
    306
    307	ret = nt35950_set_data_compression(nt, mode_data[nt->cur_mode].compression);
    308	if (ret < 0)
    309		return ret;
    310
    311	ret = nt35950_set_scale_mode(nt, mode_data[nt->cur_mode].scaler_mode);
    312	if (ret < 0)
    313		return ret;
    314
    315	ret = nt35950_set_scaler(nt, mode_data[nt->cur_mode].scaler_on);
    316	if (ret < 0)
    317		return ret;
    318
    319	ret = nt35950_set_dispout(nt);
    320	if (ret < 0)
    321		return ret;
    322
    323	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
    324	if (ret < 0) {
    325		dev_err(dev, "Failed to set tear on: %d\n", ret);
    326		return ret;
    327	}
    328
    329	ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0);
    330	if (ret < 0) {
    331		dev_err(dev, "Failed to set tear scanline: %d\n", ret);
    332		return ret;
    333	}
    334
    335	/* CMD2 Page 1 */
    336	ret = nt35950_set_cmd2_page(nt, 1);
    337	if (ret < 0)
    338		return ret;
    339
    340	/* Unknown command */
    341	dsi_dcs_write_seq(dsi, 0xd4, 0x88, 0x88);
    342
    343	/* CMD2 Page 7 */
    344	ret = nt35950_set_cmd2_page(nt, 7);
    345	if (ret < 0)
    346		return ret;
    347
    348	/* Enable SubPixel Rendering */
    349	dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_EN, 0x01);
    350
    351	/* SPR Mode: YYG Rainbow-RGB */
    352	dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_MODE, MCS_SPR_MODE_YYG_RAINBOW_RGB);
    353
    354	/* CMD3 */
    355	ret = nt35950_inject_black_image(nt);
    356	if (ret < 0)
    357		return ret;
    358
    359	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
    360	if (ret < 0)
    361		return ret;
    362	msleep(120);
    363
    364	ret = mipi_dsi_dcs_set_display_on(dsi);
    365	if (ret < 0)
    366		return ret;
    367	msleep(120);
    368
    369	nt->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM;
    370	nt->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM;
    371
    372	return 0;
    373}
    374
    375static int nt35950_off(struct nt35950 *nt)
    376{
    377	struct device *dev = &nt->dsi[0]->dev;
    378	int ret;
    379
    380	ret = mipi_dsi_dcs_set_display_off(nt->dsi[0]);
    381	if (ret < 0) {
    382		dev_err(dev, "Failed to set display off: %d\n", ret);
    383		goto set_lpm;
    384	}
    385	usleep_range(10000, 11000);
    386
    387	ret = mipi_dsi_dcs_enter_sleep_mode(nt->dsi[0]);
    388	if (ret < 0) {
    389		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
    390		goto set_lpm;
    391	}
    392	msleep(150);
    393
    394set_lpm:
    395	nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
    396	nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
    397
    398	return 0;
    399}
    400
    401static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev)
    402{
    403	int ret;
    404
    405	nt->vregs[0].supply = "vddio";
    406	nt->vregs[1].supply = "avdd";
    407	nt->vregs[2].supply = "avee";
    408	nt->vregs[3].supply = "dvdd";
    409	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(nt->vregs),
    410				      nt->vregs);
    411	if (ret < 0)
    412		return ret;
    413
    414	ret = regulator_is_supported_voltage(nt->vregs[0].consumer,
    415					     1750000, 1950000);
    416	if (!ret)
    417		return -EINVAL;
    418	ret = regulator_is_supported_voltage(nt->vregs[1].consumer,
    419					     5200000, 5900000);
    420	if (!ret)
    421		return -EINVAL;
    422	/* AVEE is negative: -5.90V to -5.20V */
    423	ret = regulator_is_supported_voltage(nt->vregs[2].consumer,
    424					     5200000, 5900000);
    425	if (!ret)
    426		return -EINVAL;
    427
    428	ret = regulator_is_supported_voltage(nt->vregs[3].consumer,
    429					     1300000, 1400000);
    430	if (!ret)
    431		return -EINVAL;
    432
    433	return 0;
    434}
    435
    436static int nt35950_prepare(struct drm_panel *panel)
    437{
    438	struct nt35950 *nt = to_nt35950(panel);
    439	struct device *dev = &nt->dsi[0]->dev;
    440	int ret;
    441
    442	if (nt->prepared)
    443		return 0;
    444
    445	ret = regulator_enable(nt->vregs[0].consumer);
    446	if (ret)
    447		return ret;
    448	usleep_range(2000, 5000);
    449
    450	ret = regulator_enable(nt->vregs[3].consumer);
    451	if (ret)
    452		goto end;
    453	usleep_range(15000, 18000);
    454
    455	ret = regulator_enable(nt->vregs[1].consumer);
    456	if (ret)
    457		goto end;
    458
    459	ret = regulator_enable(nt->vregs[2].consumer);
    460	if (ret)
    461		goto end;
    462	usleep_range(12000, 13000);
    463
    464	nt35950_reset(nt);
    465
    466	ret = nt35950_on(nt);
    467	if (ret < 0) {
    468		dev_err(dev, "Failed to initialize panel: %d\n", ret);
    469		goto end;
    470	}
    471	nt->prepared = true;
    472
    473end:
    474	if (ret < 0) {
    475		regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);
    476		return ret;
    477	}
    478
    479	return 0;
    480}
    481
    482static int nt35950_unprepare(struct drm_panel *panel)
    483{
    484	struct nt35950 *nt = to_nt35950(panel);
    485	struct device *dev = &nt->dsi[0]->dev;
    486	int ret;
    487
    488	if (!nt->prepared)
    489		return 0;
    490
    491	ret = nt35950_off(nt);
    492	if (ret < 0)
    493		dev_err(dev, "Failed to deinitialize panel: %d\n", ret);
    494
    495	gpiod_set_value_cansleep(nt->reset_gpio, 0);
    496	regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);
    497
    498	nt->prepared = false;
    499	return 0;
    500}
    501
    502static int nt35950_get_modes(struct drm_panel *panel,
    503			     struct drm_connector *connector)
    504{
    505	struct nt35950 *nt = to_nt35950(panel);
    506	int i;
    507
    508	for (i = 0; i < nt->desc->num_modes; i++) {
    509		struct drm_display_mode *mode;
    510
    511		mode = drm_mode_duplicate(connector->dev,
    512					  &nt->desc->mode_data[i].mode);
    513		if (!mode)
    514			return -ENOMEM;
    515
    516		drm_mode_set_name(mode);
    517
    518		mode->type |= DRM_MODE_TYPE_DRIVER;
    519		if (nt->desc->num_modes == 1)
    520			mode->type |= DRM_MODE_TYPE_PREFERRED;
    521
    522		drm_mode_probed_add(connector, mode);
    523	}
    524
    525	connector->display_info.bpc = 8;
    526	connector->display_info.height_mm = nt->desc->mode_data[0].mode.height_mm;
    527	connector->display_info.width_mm = nt->desc->mode_data[0].mode.width_mm;
    528	nt->connector = connector;
    529
    530	return nt->desc->num_modes;
    531}
    532
    533static const struct drm_panel_funcs nt35950_panel_funcs = {
    534	.prepare = nt35950_prepare,
    535	.unprepare = nt35950_unprepare,
    536	.get_modes = nt35950_get_modes,
    537};
    538
    539static int nt35950_probe(struct mipi_dsi_device *dsi)
    540{
    541	struct device *dev = &dsi->dev;
    542	struct device_node *dsi_r;
    543	struct mipi_dsi_host *dsi_r_host;
    544	struct nt35950 *nt;
    545	const struct mipi_dsi_device_info *info;
    546	int i, num_dsis = 1, ret;
    547
    548	nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL);
    549	if (!nt)
    550		return -ENOMEM;
    551
    552	ret = nt35950_sharp_init_vregs(nt, dev);
    553	if (ret)
    554		return dev_err_probe(dev, ret, "Regulator init failure.\n");
    555
    556	nt->desc = of_device_get_match_data(dev);
    557	if (!nt->desc)
    558		return -ENODEV;
    559
    560	nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
    561	if (IS_ERR(nt->reset_gpio)) {
    562		return dev_err_probe(dev, PTR_ERR(nt->reset_gpio),
    563				     "Failed to get reset gpio\n");
    564	}
    565
    566	/* If the panel is connected on two DSIs then DSI0 left, DSI1 right */
    567	if (nt->desc->is_dual_dsi) {
    568		info = &nt->desc->dsi_info;
    569		dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
    570		if (!dsi_r) {
    571			dev_err(dev, "Cannot get secondary DSI node.\n");
    572			return -ENODEV;
    573		}
    574		dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r);
    575		of_node_put(dsi_r);
    576		if (!dsi_r_host) {
    577			dev_err(dev, "Cannot get secondary DSI host\n");
    578			return -EPROBE_DEFER;
    579		}
    580
    581		nt->dsi[1] = mipi_dsi_device_register_full(dsi_r_host, info);
    582		if (!nt->dsi[1]) {
    583			dev_err(dev, "Cannot get secondary DSI node\n");
    584			return -ENODEV;
    585		}
    586		num_dsis++;
    587	}
    588
    589	nt->dsi[0] = dsi;
    590	mipi_dsi_set_drvdata(dsi, nt);
    591
    592	drm_panel_init(&nt->panel, dev, &nt35950_panel_funcs,
    593		       DRM_MODE_CONNECTOR_DSI);
    594
    595	ret = drm_panel_of_backlight(&nt->panel);
    596	if (ret)
    597		return dev_err_probe(dev, ret, "Failed to get backlight\n");
    598
    599	drm_panel_add(&nt->panel);
    600
    601	for (i = 0; i < num_dsis; i++) {
    602		nt->dsi[i]->lanes = nt->desc->num_lanes;
    603		nt->dsi[i]->format = MIPI_DSI_FMT_RGB888;
    604
    605		nt->dsi[i]->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |
    606					 MIPI_DSI_MODE_LPM;
    607
    608		if (nt->desc->mode_data[0].is_video_mode)
    609			nt->dsi[i]->mode_flags |= MIPI_DSI_MODE_VIDEO;
    610
    611		ret = mipi_dsi_attach(nt->dsi[i]);
    612		if (ret < 0) {
    613			return dev_err_probe(dev, ret,
    614					     "Cannot attach to DSI%d host.\n", i);
    615		}
    616	}
    617
    618	/* Make sure to set RESX LOW before starting the power-on sequence */
    619	gpiod_set_value_cansleep(nt->reset_gpio, 0);
    620	return 0;
    621}
    622
    623static int nt35950_remove(struct mipi_dsi_device *dsi)
    624{
    625	struct nt35950 *nt = mipi_dsi_get_drvdata(dsi);
    626	int ret;
    627
    628	ret = mipi_dsi_detach(nt->dsi[0]);
    629	if (ret < 0)
    630		dev_err(&dsi->dev,
    631			"Failed to detach from DSI0 host: %d\n", ret);
    632
    633	if (nt->dsi[1]) {
    634		ret = mipi_dsi_detach(nt->dsi[1]);
    635		if (ret < 0)
    636			dev_err(&dsi->dev,
    637				"Failed to detach from DSI1 host: %d\n", ret);
    638		mipi_dsi_device_unregister(nt->dsi[1]);
    639	}
    640
    641	drm_panel_remove(&nt->panel);
    642
    643	return 0;
    644}
    645
    646static const struct nt35950_panel_mode sharp_ls055d1sx04_modes[] = {
    647	{
    648		/* 1920x1080 60Hz no compression */
    649		.mode = {
    650			.clock = 214537,
    651			.hdisplay = 1080,
    652			.hsync_start = 1080 + 400,
    653			.hsync_end = 1080 + 400 + 40,
    654			.htotal = 1080 + 400 + 40 + 300,
    655			.vdisplay = 1920,
    656			.vsync_start = 1920 + 12,
    657			.vsync_end = 1920 + 12 + 2,
    658			.vtotal = 1920 + 12 + 2 + 10,
    659			.width_mm = 68,
    660			.height_mm = 121,
    661		},
    662		.compression = MCS_DATA_COMPRESSION_NONE,
    663		.enable_sram = true,
    664		.is_video_mode = false,
    665		.scaler_on = 1,
    666		.scaler_mode = MCS_SCALEUP_DUPLICATE,
    667	},
    668	/* TODO: Add 2160x3840 60Hz when DSC is supported */
    669};
    670
    671static const struct nt35950_panel_desc sharp_ls055d1sx04 = {
    672	.model_name = "Sharp LS055D1SX04",
    673	.dsi_info = {
    674		.type = "LS055D1SX04",
    675		.channel = 0,
    676		.node = NULL,
    677	},
    678	.mode_data = sharp_ls055d1sx04_modes,
    679	.num_modes = ARRAY_SIZE(sharp_ls055d1sx04_modes),
    680	.is_dual_dsi = true,
    681	.num_lanes = 4,
    682};
    683
    684static const struct of_device_id nt35950_of_match[] = {
    685	{ .compatible = "sharp,ls055d1sx04", .data = &sharp_ls055d1sx04 },
    686	{  }
    687};
    688MODULE_DEVICE_TABLE(of, nt35950_of_match);
    689
    690static struct mipi_dsi_driver nt35950_driver = {
    691	.probe = nt35950_probe,
    692	.remove = nt35950_remove,
    693	.driver = {
    694		.name = "panel-novatek-nt35950",
    695		.of_match_table = nt35950_of_match,
    696	},
    697};
    698module_mipi_dsi_driver(nt35950_driver);
    699
    700MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>");
    701MODULE_DESCRIPTION("Novatek NT35950 DriverIC panels driver");
    702MODULE_LICENSE("GPL v2");