brintos

brintos / linux-shallow public Read only

0
0
Text · 16.0 KiB · b036208 Raw
611 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Novatek NT35950 DriverIC panels driver4 *5 * Copyright (c) 2021 AngeloGioacchino Del Regno6 *                    <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.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		0xc924 #define MCS_SCALEUP_SIMPLE		0x025 #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		0x0330 31/* Data Compression mode */32#define MCS_PARAM_DATA_COMPRESSION	0x9033 #define MCS_DATA_COMPRESSION_NONE	0x0034 #define MCS_DATA_COMPRESSION_FBC	0x0235 #define MCS_DATA_COMPRESSION_DSC	0x0336 37/* Display Output control */38#define MCS_PARAM_DISP_OUTPUT_CTRL	0xb439 #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	0xc044 45/* SubPixel Rendering (SPR) */46#define MCS_PARAM_SPR_EN		0xe347#define MCS_PARAM_SPR_MODE		0xef48 #define MCS_SPR_MODE_YYG_RAINBOW_RGB	0x0149 50#define NT35950_VREG_MAX		451 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};63 64struct nt35950_panel_mode {65	const struct drm_display_mode mode;66 67	bool enable_sram;68	bool is_video_mode;69	u8 scaler_on;70	u8 scaler_mode;71	u8 compression;72	u8 spr_en;73	u8 spr_mode;74};75 76struct nt35950_panel_desc {77	const char *model_name;78	const struct mipi_dsi_device_info dsi_info;79	const struct nt35950_panel_mode *mode_data;80 81	bool is_dual_dsi;82	u8 num_lanes;83	u8 num_modes;84};85 86static inline struct nt35950 *to_nt35950(struct drm_panel *panel)87{88	return container_of(panel, struct nt35950, panel);89}90 91static void nt35950_reset(struct nt35950 *nt)92{93	gpiod_set_value_cansleep(nt->reset_gpio, 1);94	usleep_range(12000, 13000);95	gpiod_set_value_cansleep(nt->reset_gpio, 0);96	usleep_range(300, 400);97	gpiod_set_value_cansleep(nt->reset_gpio, 1);98	usleep_range(12000, 13000);99}100 101/*102 * nt35950_set_cmd2_page - Select manufacturer control (CMD2) page103 * @dsi_ctx: context for mipi_dsi functions104 * @nt:   Main driver structure105 * @page: Page number (0-7)106 */107static void nt35950_set_cmd2_page(struct mipi_dsi_multi_context *dsi_ctx,108				  struct nt35950 *nt, u8 page)109{110	const u8 mauc_cmd2_page[] = { MCS_CMD_MAUCCTR, 0x55, 0xaa, 0x52,111				      0x08, page };112 113	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, mauc_cmd2_page,114					ARRAY_SIZE(mauc_cmd2_page));115	if (!dsi_ctx->accum_err)116		nt->last_page = page;117}118 119/*120 * nt35950_set_data_compression - Set data compression mode121 * @dsi_ctx: context for mipi_dsi functions122 * @nt:        Main driver structure123 * @comp_mode: Compression mode124 */125static void nt35950_set_data_compression(struct mipi_dsi_multi_context *dsi_ctx,126					 struct nt35950 *nt, u8 comp_mode)127{128	u8 cmd_data_compression[] = { MCS_PARAM_DATA_COMPRESSION, comp_mode };129	u8 cmd_vesa_dsc_on[] = { MCS_PARAM_VESA_DSC_ON, !!comp_mode };130	u8 cmd_vesa_dsc_setting[] = { MCS_PARAM_VESA_DSC_SETTING, 0x03 };131	u8 last_page = nt->last_page;132 133	/* Set CMD2 Page 0 if we're not there yet */134	if (last_page != 0)135		nt35950_set_cmd2_page(dsi_ctx, nt, 0);136 137	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_data_compression,138					ARRAY_SIZE(cmd_data_compression));139	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_vesa_dsc_on,140					ARRAY_SIZE(cmd_vesa_dsc_on));141 142	/* Set the vesa dsc setting on Page 4 */143	nt35950_set_cmd2_page(dsi_ctx, nt, 4);144 145	/* Display Stream Compression setting, always 0x03 */146	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_vesa_dsc_setting,147					ARRAY_SIZE(cmd_vesa_dsc_setting));148 149	/* Get back to the previously set page */150	nt35950_set_cmd2_page(dsi_ctx, nt, last_page);151}152 153/*154 * nt35950_set_scaler - Enable/disable resolution upscaling155 * @dsi_ctx: context for mipi_dsi functions156 * @scale_up:  Scale up function control157 */158static void nt35950_set_scaler(struct mipi_dsi_multi_context *dsi_ctx,159			       u8 scale_up)160{161	u8 cmd_scaler[] = { MCS_PARAM_SCALER_FUNCTION, scale_up };162 163	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_scaler,164					ARRAY_SIZE(cmd_scaler));165}166 167/*168 * nt35950_set_scale_mode - Resolution upscaling mode169 * @dsi_ctx: context for mipi_dsi functions170 * @mode: Scaler mode (MCS_DATA_COMPRESSION_*)171 */172static void nt35950_set_scale_mode(struct mipi_dsi_multi_context *dsi_ctx,173				   u8 mode)174{175	u8 cmd_scaler[] = { MCS_PARAM_SCALEUP_MODE, mode };176 177	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_scaler,178					ARRAY_SIZE(cmd_scaler));179}180 181/*182 * nt35950_inject_black_image - Display a completely black image183 * @dsi_ctx: context for mipi_dsi functions184 *185 * After IC setup, the attached panel may show random data186 * due to driveric behavior changes (resolution, compression,187 * scaling, etc). This function, called after parameters setup,188 * makes the driver ic to output a completely black image to189 * the display.190 * It makes sense to push a black image before sending the sleep-out191 * and display-on commands.192 */193static void nt35950_inject_black_image(struct mipi_dsi_multi_context *dsi_ctx)194{195	const u8 cmd0_black_img[] = { 0x6f, 0x01 };196	const u8 cmd1_black_img[] = { 0xf3, 0x10 };197	u8 cmd_test[] = { 0xff, 0xaa, 0x55, 0xa5, 0x80 };198 199	/* Enable test command */200	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_test, ARRAY_SIZE(cmd_test));201 202	/* Send a black image */203	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd0_black_img,204					ARRAY_SIZE(cmd0_black_img));205	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd1_black_img,206					ARRAY_SIZE(cmd1_black_img));207 208	/* Disable test command */209	cmd_test[ARRAY_SIZE(cmd_test) - 1] = 0x00;210	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_test, ARRAY_SIZE(cmd_test));211}212 213/*214 * nt35950_set_dispout - Set Display Output register parameters215 * @nt:    Main driver structure216 * @dsi_ctx: context for mipi_dsi functions217 */218static void nt35950_set_dispout(struct mipi_dsi_multi_context *dsi_ctx,219				struct nt35950 *nt)220{221	u8 cmd_dispout[] = { MCS_PARAM_DISP_OUTPUT_CTRL, 0x00 };222	const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;223 224	if (mode_data[nt->cur_mode].is_video_mode)225		cmd_dispout[1] |= MCS_DISP_OUT_VIDEO_MODE;226	if (mode_data[nt->cur_mode].enable_sram)227		cmd_dispout[1] |= MCS_DISP_OUT_SRAM_EN;228 229	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_dispout,230					ARRAY_SIZE(cmd_dispout));231}232 233static int nt35950_get_current_mode(struct nt35950 *nt)234{235	struct drm_connector *connector = nt->connector;236	struct drm_crtc_state *crtc_state;237	int i;238 239	/* Return the default (first) mode if no info available yet */240	if (!connector->state || !connector->state->crtc)241		return 0;242 243	crtc_state = connector->state->crtc->state;244 245	for (i = 0; i < nt->desc->num_modes; i++) {246		if (drm_mode_match(&crtc_state->mode,247				   &nt->desc->mode_data[i].mode,248				   DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK))249			return i;250	}251 252	return 0;253}254 255static int nt35950_on(struct nt35950 *nt)256{257	const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;258	struct mipi_dsi_device *dsi = nt->dsi[0];259	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };260 261	nt->cur_mode = nt35950_get_current_mode(nt);262	nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;263	nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;264 265	nt35950_set_cmd2_page(&dsi_ctx, nt, 0);266	nt35950_set_data_compression(&dsi_ctx, nt, mode_data[nt->cur_mode].compression);267	nt35950_set_scale_mode(&dsi_ctx, mode_data[nt->cur_mode].scaler_mode);268	nt35950_set_scaler(&dsi_ctx, mode_data[nt->cur_mode].scaler_on);269	nt35950_set_dispout(&dsi_ctx, nt);270 271	mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);272	mipi_dsi_dcs_set_tear_scanline_multi(&dsi_ctx, 0);273 274	/* CMD2 Page 1 */275	nt35950_set_cmd2_page(&dsi_ctx, nt, 1);276 277	/* Unknown command */278	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x88, 0x88);279 280	/* CMD2 Page 7 */281	nt35950_set_cmd2_page(&dsi_ctx, nt, 7);282 283	/* Enable SubPixel Rendering */284	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PARAM_SPR_EN, 0x01);285 286	/* SPR Mode: YYG Rainbow-RGB */287	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PARAM_SPR_MODE,288				     MCS_SPR_MODE_YYG_RAINBOW_RGB);289 290	/* CMD3 */291	nt35950_inject_black_image(&dsi_ctx);292	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);293	mipi_dsi_msleep(&dsi_ctx, 120);294 295	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);296	mipi_dsi_msleep(&dsi_ctx, 120);297 298	if (dsi_ctx.accum_err)299		return dsi_ctx.accum_err;300 301	nt->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM;302	nt->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM;303 304	return 0;305}306 307static void nt35950_off(struct nt35950 *nt)308{309	struct mipi_dsi_device *dsi = nt->dsi[0];310	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };311 312	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);313	mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);314 315	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);316	mipi_dsi_msleep(&dsi_ctx, 150);317 318	nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;319	nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;320}321 322static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev)323{324	int ret;325 326	nt->vregs[0].supply = "vddio";327	nt->vregs[1].supply = "avdd";328	nt->vregs[2].supply = "avee";329	nt->vregs[3].supply = "dvdd";330	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(nt->vregs),331				      nt->vregs);332	if (ret < 0)333		return ret;334 335	ret = regulator_is_supported_voltage(nt->vregs[0].consumer,336					     1750000, 1950000);337	if (!ret)338		return -EINVAL;339	ret = regulator_is_supported_voltage(nt->vregs[1].consumer,340					     5200000, 5900000);341	if (!ret)342		return -EINVAL;343	/* AVEE is negative: -5.90V to -5.20V */344	ret = regulator_is_supported_voltage(nt->vregs[2].consumer,345					     5200000, 5900000);346	if (!ret)347		return -EINVAL;348 349	ret = regulator_is_supported_voltage(nt->vregs[3].consumer,350					     1300000, 1400000);351	if (!ret)352		return -EINVAL;353 354	return 0;355}356 357static int nt35950_prepare(struct drm_panel *panel)358{359	struct nt35950 *nt = to_nt35950(panel);360	int ret;361 362	ret = regulator_enable(nt->vregs[0].consumer);363	if (ret)364		return ret;365	usleep_range(2000, 5000);366 367	ret = regulator_enable(nt->vregs[3].consumer);368	if (ret)369		goto end;370	usleep_range(15000, 18000);371 372	ret = regulator_enable(nt->vregs[1].consumer);373	if (ret)374		goto end;375 376	ret = regulator_enable(nt->vregs[2].consumer);377	if (ret)378		goto end;379	usleep_range(12000, 13000);380 381	nt35950_reset(nt);382 383	ret = nt35950_on(nt);384 385end:386	if (ret < 0) {387		regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);388		return ret;389	}390 391	return 0;392}393 394static int nt35950_unprepare(struct drm_panel *panel)395{396	struct nt35950 *nt = to_nt35950(panel);397 398	nt35950_off(nt);399 400	gpiod_set_value_cansleep(nt->reset_gpio, 0);401	regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);402 403	return 0;404}405 406static int nt35950_get_modes(struct drm_panel *panel,407			     struct drm_connector *connector)408{409	struct nt35950 *nt = to_nt35950(panel);410	int i;411 412	for (i = 0; i < nt->desc->num_modes; i++) {413		struct drm_display_mode *mode;414 415		mode = drm_mode_duplicate(connector->dev,416					  &nt->desc->mode_data[i].mode);417		if (!mode)418			return -ENOMEM;419 420		drm_mode_set_name(mode);421 422		mode->type |= DRM_MODE_TYPE_DRIVER;423		if (nt->desc->num_modes == 1)424			mode->type |= DRM_MODE_TYPE_PREFERRED;425 426		drm_mode_probed_add(connector, mode);427	}428 429	connector->display_info.bpc = 8;430	connector->display_info.height_mm = nt->desc->mode_data[0].mode.height_mm;431	connector->display_info.width_mm = nt->desc->mode_data[0].mode.width_mm;432	nt->connector = connector;433 434	return nt->desc->num_modes;435}436 437static const struct drm_panel_funcs nt35950_panel_funcs = {438	.prepare = nt35950_prepare,439	.unprepare = nt35950_unprepare,440	.get_modes = nt35950_get_modes,441};442 443static int nt35950_probe(struct mipi_dsi_device *dsi)444{445	struct device *dev = &dsi->dev;446	struct device_node *dsi_r;447	struct mipi_dsi_host *dsi_r_host;448	struct nt35950 *nt;449	const struct mipi_dsi_device_info *info;450	int i, num_dsis = 1, ret;451 452	nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL);453	if (!nt)454		return -ENOMEM;455 456	ret = nt35950_sharp_init_vregs(nt, dev);457	if (ret)458		return dev_err_probe(dev, ret, "Regulator init failure.\n");459 460	nt->desc = of_device_get_match_data(dev);461	if (!nt->desc)462		return -ENODEV;463 464	nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);465	if (IS_ERR(nt->reset_gpio)) {466		return dev_err_probe(dev, PTR_ERR(nt->reset_gpio),467				     "Failed to get reset gpio\n");468	}469 470	/* If the panel is connected on two DSIs then DSI0 left, DSI1 right */471	if (nt->desc->is_dual_dsi) {472		info = &nt->desc->dsi_info;473		dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);474		if (!dsi_r) {475			dev_err(dev, "Cannot get secondary DSI node.\n");476			return -ENODEV;477		}478		dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r);479		of_node_put(dsi_r);480		if (!dsi_r_host)481			return dev_err_probe(dev, -EPROBE_DEFER, "Cannot get secondary DSI host\n");482 483		nt->dsi[1] = mipi_dsi_device_register_full(dsi_r_host, info);484		if (!nt->dsi[1]) {485			dev_err(dev, "Cannot get secondary DSI node\n");486			return -ENODEV;487		}488		num_dsis++;489	}490 491	nt->dsi[0] = dsi;492	mipi_dsi_set_drvdata(dsi, nt);493 494	drm_panel_init(&nt->panel, dev, &nt35950_panel_funcs,495		       DRM_MODE_CONNECTOR_DSI);496 497	ret = drm_panel_of_backlight(&nt->panel);498	if (ret) {499		if (num_dsis == 2)500			mipi_dsi_device_unregister(nt->dsi[1]);501 502		return dev_err_probe(dev, ret, "Failed to get backlight\n");503	}504 505	drm_panel_add(&nt->panel);506 507	for (i = 0; i < num_dsis; i++) {508		nt->dsi[i]->lanes = nt->desc->num_lanes;509		nt->dsi[i]->format = MIPI_DSI_FMT_RGB888;510 511		nt->dsi[i]->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |512					 MIPI_DSI_MODE_LPM;513 514		if (nt->desc->mode_data[0].is_video_mode)515			nt->dsi[i]->mode_flags |= MIPI_DSI_MODE_VIDEO;516 517		ret = mipi_dsi_attach(nt->dsi[i]);518		if (ret < 0) {519			/* If we fail to attach to either host, we're done */520			if (num_dsis == 2)521				mipi_dsi_device_unregister(nt->dsi[1]);522 523			return dev_err_probe(dev, ret,524					     "Cannot attach to DSI%d host.\n", i);525		}526	}527 528	/* Make sure to set RESX LOW before starting the power-on sequence */529	gpiod_set_value_cansleep(nt->reset_gpio, 0);530	return 0;531}532 533static void nt35950_remove(struct mipi_dsi_device *dsi)534{535	struct nt35950 *nt = mipi_dsi_get_drvdata(dsi);536	int ret;537 538	ret = mipi_dsi_detach(nt->dsi[0]);539	if (ret < 0)540		dev_err(&dsi->dev,541			"Failed to detach from DSI0 host: %d\n", ret);542 543	if (nt->dsi[1]) {544		ret = mipi_dsi_detach(nt->dsi[1]);545		if (ret < 0)546			dev_err(&dsi->dev,547				"Failed to detach from DSI1 host: %d\n", ret);548		mipi_dsi_device_unregister(nt->dsi[1]);549	}550 551	drm_panel_remove(&nt->panel);552}553 554static const struct nt35950_panel_mode sharp_ls055d1sx04_modes[] = {555	{556		/* 1920x1080 60Hz no compression */557		.mode = {558			.clock = 214537,559			.hdisplay = 1080,560			.hsync_start = 1080 + 400,561			.hsync_end = 1080 + 400 + 40,562			.htotal = 1080 + 400 + 40 + 300,563			.vdisplay = 1920,564			.vsync_start = 1920 + 12,565			.vsync_end = 1920 + 12 + 2,566			.vtotal = 1920 + 12 + 2 + 10,567			.width_mm = 68,568			.height_mm = 121,569		},570		.compression = MCS_DATA_COMPRESSION_NONE,571		.enable_sram = true,572		.is_video_mode = false,573		.scaler_on = 1,574		.scaler_mode = MCS_SCALEUP_DUPLICATE,575	},576	/* TODO: Add 2160x3840 60Hz when DSC is supported */577};578 579static const struct nt35950_panel_desc sharp_ls055d1sx04 = {580	.model_name = "Sharp LS055D1SX04",581	.dsi_info = {582		.type = "LS055D1SX04",583		.channel = 0,584		.node = NULL,585	},586	.mode_data = sharp_ls055d1sx04_modes,587	.num_modes = ARRAY_SIZE(sharp_ls055d1sx04_modes),588	.is_dual_dsi = true,589	.num_lanes = 4,590};591 592static const struct of_device_id nt35950_of_match[] = {593	{ .compatible = "sharp,ls055d1sx04", .data = &sharp_ls055d1sx04 },594	{  }595};596MODULE_DEVICE_TABLE(of, nt35950_of_match);597 598static struct mipi_dsi_driver nt35950_driver = {599	.probe = nt35950_probe,600	.remove = nt35950_remove,601	.driver = {602		.name = "panel-novatek-nt35950",603		.of_match_table = nt35950_of_match,604	},605};606module_mipi_dsi_driver(nt35950_driver);607 608MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>");609MODULE_DESCRIPTION("Novatek NT35950 DriverIC panels driver");610MODULE_LICENSE("GPL v2");611