brintos

brintos / linux-shallow public Read only

0
0
Text · 12.3 KiB · ed53787 Raw
523 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver.4 *5 * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd6 *7 * Inki Dae <inki.dae@samsung.com>8 * Hoegeun Kwon <hoegeun.kwon@samsung.com>9 */10 11#include <linux/backlight.h>12#include <linux/delay.h>13#include <linux/gpio/consumer.h>14#include <linux/module.h>15#include <linux/regulator/consumer.h>16 17#include <video/mipi_display.h>18 19#include <drm/drm_mipi_dsi.h>20#include <drm/drm_modes.h>21#include <drm/drm_panel.h>22 23#define MCS_LEVEL2_KEY		0xf024#define MCS_MTP_KEY		0xf125#define MCS_MTP_SET3		0xd426 27#define MAX_BRIGHTNESS		10028#define DEFAULT_BRIGHTNESS	8029 30#define NUM_GAMMA_STEPS		931#define GAMMA_CMD_CNT		2832 33#define FIRST_COLUMN 2034 35struct s6e63j0x03 {36	struct device *dev;37	struct drm_panel panel;38	struct backlight_device *bl_dev;39 40	struct regulator_bulk_data supplies[2];41	struct gpio_desc *reset_gpio;42};43 44static const struct drm_display_mode default_mode = {45	.clock = 4649,46	.hdisplay = 320,47	.hsync_start = 320 + 1,48	.hsync_end = 320 + 1 + 1,49	.htotal = 320 + 1 + 1 + 1,50	.vdisplay = 320,51	.vsync_start = 320 + 150,52	.vsync_end = 320 + 150 + 1,53	.vtotal = 320 + 150 + 1 + 2,54	.flags = 0,55};56 57static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = {58	{	/* Gamma 10 */59		MCS_MTP_SET3,60		0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,61		0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,62		0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf63	},64	{	/* gamma 30 */65		MCS_MTP_SET3,66		0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,67		0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,68		0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc69	},70	{	/* gamma 60 */71		MCS_MTP_SET3,72		0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,73		0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,74		0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf575	},76	{	/* gamma 90 */77		MCS_MTP_SET3,78		0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,79		0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,80		0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x0981	},82	{	/* gamma 120 */83		MCS_MTP_SET3,84		0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,85		0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,86		0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b87	},88	{	/* gamma 150 */89		MCS_MTP_SET3,90		0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,91		0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,92		0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x2993	},94	{	/* gamma 200 */95		MCS_MTP_SET3,96		0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,97		0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,98		0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b99	},100	{	/* gamma 240 */101		MCS_MTP_SET3,102		0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,103		0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,104		0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47105	},106	{	/* gamma 300 */107		MCS_MTP_SET3,108		0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,109		0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,110		0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57111	}112};113 114static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)115{116	return container_of(panel, struct s6e63j0x03, panel);117}118 119static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,120					const void *seq, size_t len)121{122	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);123 124	return mipi_dsi_dcs_write_buffer(dsi, seq, len);125}126 127#define s6e63j0x03_dcs_write_seq_static(ctx, seq...)			\128	({								\129		static const u8 d[] = { seq };				\130		s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d));	\131	})132 133static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)134{135	return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);136}137 138static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)139{140	if (on)141		return s6e63j0x03_dcs_write_seq_static(ctx,142				MCS_MTP_KEY, 0x5a, 0x5a);143 144	return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);145}146 147static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)148{149	int ret;150 151	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);152	if (ret < 0)153		return ret;154 155	msleep(30);156 157	gpiod_set_value(ctx->reset_gpio, 1);158	usleep_range(1000, 2000);159	gpiod_set_value(ctx->reset_gpio, 0);160	usleep_range(5000, 6000);161 162	return 0;163}164 165static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)166{167	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);168}169 170static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)171{172	unsigned int index;173 174	index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);175 176	if (index >= NUM_GAMMA_STEPS)177		index = NUM_GAMMA_STEPS - 1;178 179	return index;180}181 182static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,183					unsigned int brightness)184{185	struct backlight_device *bl_dev = ctx->bl_dev;186	unsigned int index = s6e63j0x03_get_brightness_index(brightness);187	int ret;188 189	ret = s6e63j0x03_apply_mtp_key(ctx, true);190	if (ret < 0)191		return ret;192 193	ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);194	if (ret < 0)195		return ret;196 197	ret = s6e63j0x03_apply_mtp_key(ctx, false);198	if (ret < 0)199		return ret;200 201	bl_dev->props.brightness = brightness;202 203	return 0;204}205 206static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)207{208	struct s6e63j0x03 *ctx = bl_get_data(bl_dev);209	unsigned int brightness = bl_dev->props.brightness;210 211	return s6e63j0x03_update_gamma(ctx, brightness);212}213 214static const struct backlight_ops s6e63j0x03_bl_ops = {215	.update_status = s6e63j0x03_set_brightness,216};217 218static int s6e63j0x03_disable(struct drm_panel *panel)219{220	struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);221	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);222	int ret;223 224	ret = mipi_dsi_dcs_set_display_off(dsi);225	if (ret < 0)226		return ret;227 228	ctx->bl_dev->props.power = BACKLIGHT_POWER_REDUCED;229 230	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);231	if (ret < 0)232		return ret;233 234	msleep(120);235 236	return 0;237}238 239static int s6e63j0x03_unprepare(struct drm_panel *panel)240{241	struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);242	int ret;243 244	ret = s6e63j0x03_power_off(ctx);245	if (ret < 0)246		return ret;247 248	ctx->bl_dev->props.power = BACKLIGHT_POWER_OFF;249 250	return 0;251}252 253static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)254{255	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);256	int ret;257 258	ret = s6e63j0x03_enable_lv2_command(ctx);259	if (ret < 0)260		return ret;261 262	ret = s6e63j0x03_apply_mtp_key(ctx, true);263	if (ret < 0)264		return ret;265 266	/* set porch adjustment */267	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);268	if (ret < 0)269		return ret;270 271	/* set frame freq */272	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);273	if (ret < 0)274		return ret;275 276	/* set caset, paset */277	ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN,278		default_mode.hdisplay - 1 + FIRST_COLUMN);279	if (ret < 0)280		return ret;281 282	ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);283	if (ret < 0)284		return ret;285 286	/* set ltps timming 0, 1 */287	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17,288		0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);289	if (ret < 0)290		return ret;291 292	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);293	if (ret < 0)294		return ret;295 296	/* set param pos te_edge */297	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);298	if (ret < 0)299		return ret;300 301	/* set te rising edge */302	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);303	if (ret < 0)304		return ret;305 306	/* set param pos default */307	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);308	if (ret < 0)309		return ret;310 311	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);312	if (ret < 0)313		return ret;314 315	ret = s6e63j0x03_apply_mtp_key(ctx, false);316	if (ret < 0)317		return ret;318 319	return 0;320}321 322static int s6e63j0x03_prepare(struct drm_panel *panel)323{324	struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);325	int ret;326 327	ret = s6e63j0x03_power_on(ctx);328	if (ret < 0)329		return ret;330 331	ret = s6e63j0x03_panel_init(ctx);332	if (ret < 0)333		goto err;334 335	ctx->bl_dev->props.power = BACKLIGHT_POWER_REDUCED;336 337	return 0;338 339err:340	s6e63j0x03_power_off(ctx);341	return ret;342}343 344static int s6e63j0x03_enable(struct drm_panel *panel)345{346	struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);347	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);348	int ret;349 350	msleep(120);351 352	ret = s6e63j0x03_apply_mtp_key(ctx, true);353	if (ret < 0)354		return ret;355 356	/* set elvss_cond */357	ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);358	if (ret < 0)359		return ret;360 361	/* set pos */362	ret = s6e63j0x03_dcs_write_seq_static(ctx,363		MIPI_DCS_SET_ADDRESS_MODE, 0x40);364	if (ret < 0)365		return ret;366 367	/* set default white brightness */368	ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);369	if (ret < 0)370		return ret;371 372	/* set white ctrl */373	ret = s6e63j0x03_dcs_write_seq_static(ctx,374		MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);375	if (ret < 0)376		return ret;377 378	/* set acl off */379	ret = s6e63j0x03_dcs_write_seq_static(ctx,380		MIPI_DCS_WRITE_POWER_SAVE, 0x00);381	if (ret < 0)382		return ret;383 384	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);385	if (ret < 0)386		return ret;387 388	ret = s6e63j0x03_apply_mtp_key(ctx, false);389	if (ret < 0)390		return ret;391 392	ret = mipi_dsi_dcs_set_display_on(dsi);393	if (ret < 0)394		return ret;395 396	ctx->bl_dev->props.power = BACKLIGHT_POWER_ON;397 398	return 0;399}400 401static int s6e63j0x03_get_modes(struct drm_panel *panel,402				struct drm_connector *connector)403{404	struct drm_display_mode *mode;405 406	mode = drm_mode_duplicate(connector->dev, &default_mode);407	if (!mode) {408		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",409			default_mode.hdisplay, default_mode.vdisplay,410			drm_mode_vrefresh(&default_mode));411		return -ENOMEM;412	}413 414	drm_mode_set_name(mode);415 416	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;417	drm_mode_probed_add(connector, mode);418 419	connector->display_info.width_mm = 29;420	connector->display_info.height_mm = 29;421 422	return 1;423}424 425static const struct drm_panel_funcs s6e63j0x03_funcs = {426	.disable = s6e63j0x03_disable,427	.unprepare = s6e63j0x03_unprepare,428	.prepare = s6e63j0x03_prepare,429	.enable = s6e63j0x03_enable,430	.get_modes = s6e63j0x03_get_modes,431};432 433static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)434{435	struct device *dev = &dsi->dev;436	struct s6e63j0x03 *ctx;437	int ret;438 439	ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);440	if (!ctx)441		return -ENOMEM;442 443	mipi_dsi_set_drvdata(dsi, ctx);444 445	ctx->dev = dev;446 447	dsi->lanes = 1;448	dsi->format = MIPI_DSI_FMT_RGB888;449	dsi->mode_flags = MIPI_DSI_MODE_VIDEO_NO_HFP |450		MIPI_DSI_MODE_VIDEO_NO_HBP | MIPI_DSI_MODE_VIDEO_NO_HSA;451 452	ctx->supplies[0].supply = "vdd3";453	ctx->supplies[1].supply = "vci";454	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),455				      ctx->supplies);456	if (ret < 0)457		return dev_err_probe(dev, ret, "failed to get regulators\n");458 459	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);460	if (IS_ERR(ctx->reset_gpio))461		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),462				     "cannot get reset-gpio\n");463 464	drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs,465		       DRM_MODE_CONNECTOR_DSI);466	ctx->panel.prepare_prev_first = true;467 468	ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,469						&s6e63j0x03_bl_ops, NULL);470	if (IS_ERR(ctx->bl_dev))471		return dev_err_probe(dev, PTR_ERR(ctx->bl_dev),472				     "failed to register backlight device\n");473 474	ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;475	ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;476	ctx->bl_dev->props.power = BACKLIGHT_POWER_OFF;477 478	drm_panel_add(&ctx->panel);479 480	ret = mipi_dsi_attach(dsi);481	if (ret < 0)482		goto remove_panel;483 484	return ret;485 486remove_panel:487	drm_panel_remove(&ctx->panel);488	backlight_device_unregister(ctx->bl_dev);489 490	return ret;491}492 493static void s6e63j0x03_remove(struct mipi_dsi_device *dsi)494{495	struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);496 497	mipi_dsi_detach(dsi);498	drm_panel_remove(&ctx->panel);499 500	backlight_device_unregister(ctx->bl_dev);501}502 503static const struct of_device_id s6e63j0x03_of_match[] = {504	{ .compatible = "samsung,s6e63j0x03" },505	{ }506};507MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);508 509static struct mipi_dsi_driver s6e63j0x03_driver = {510	.probe = s6e63j0x03_probe,511	.remove = s6e63j0x03_remove,512	.driver = {513		.name = "panel_samsung_s6e63j0x03",514		.of_match_table = s6e63j0x03_of_match,515	},516};517module_mipi_dsi_driver(s6e63j0x03_driver);518 519MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");520MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");521MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");522MODULE_LICENSE("GPL v2");523