brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · c0c9535 Raw
351 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2016 InforceComputing4 * Copyright (C) 2016 Linaro Ltd5 * Copyright (C) 2023 BayLibre, SAS6 *7 * Authors:8 * - Vinay Simha BN <simhavcs@gmail.com>9 * - Sumit Semwal <sumit.semwal@linaro.org>10 * - Guillaume La Roque <glaroque@baylibre.com>11 *12 */13 14#include <linux/backlight.h>15#include <linux/delay.h>16#include <linux/gpio/consumer.h>17#include <linux/module.h>18#include <linux/of.h>19#include <linux/regulator/consumer.h>20 21#include <video/mipi_display.h>22 23#include <drm/drm_mipi_dsi.h>24#include <drm/drm_modes.h>25#include <drm/drm_panel.h>26 27#define DSI_REG_MCAP	0xb028#define DSI_REG_IS	0xb3 /* Interface Setting */29#define DSI_REG_IIS	0xb4 /* Interface ID Setting */30#define DSI_REG_CTRL	0xb631 32enum {33	IOVCC = 0,34	POWER = 135};36 37struct stk_panel {38	const struct drm_display_mode *mode;39	struct backlight_device *backlight;40	struct drm_panel base;41	struct gpio_desc *enable_gpio; /* Power IC supply enable */42	struct gpio_desc *reset_gpio; /* External reset */43	struct mipi_dsi_device *dsi;44	struct regulator_bulk_data supplies[2];45};46 47static inline struct stk_panel *to_stk_panel(struct drm_panel *panel)48{49	return container_of(panel, struct stk_panel, base);50}51 52static int stk_panel_init(struct stk_panel *stk)53{54	struct mipi_dsi_device *dsi = stk->dsi;55	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};56 57	mipi_dsi_dcs_soft_reset_multi(&dsi_ctx);58	mipi_dsi_msleep(&dsi_ctx, 5);59	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);60	mipi_dsi_msleep(&dsi_ctx, 120);61 62	mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_MCAP, 0x04);63 64	/* Interface setting, video mode */65	mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00);66	mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_IIS, 0x0c, 0x00);67	mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_CTRL, 0x3a, 0xd3);68 69	mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x77);70 71	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,72				     MIPI_DCS_WRITE_MEMORY_START);73 74	mipi_dsi_dcs_set_pixel_format_multi(&dsi_ctx, 0x77);75	mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, stk->mode->hdisplay - 1);76	mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, stk->mode->vdisplay - 1);77 78	return dsi_ctx.accum_err;79}80 81static int stk_panel_on(struct stk_panel *stk)82{83	struct mipi_dsi_device *dsi = stk->dsi;84	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};85 86	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);87 88	mipi_dsi_msleep(&dsi_ctx, 20);89 90	return dsi_ctx.accum_err;91}92 93static void stk_panel_off(struct stk_panel *stk)94{95	struct mipi_dsi_device *dsi = stk->dsi;96	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};97 98	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;99 100	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);101	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);102 103	mipi_dsi_msleep(&dsi_ctx, 100);104}105 106static int stk_panel_unprepare(struct drm_panel *panel)107{108	struct stk_panel *stk = to_stk_panel(panel);109 110	stk_panel_off(stk);111	regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies);112	gpiod_set_value(stk->reset_gpio, 0);113	gpiod_set_value(stk->enable_gpio, 1);114 115	return 0;116}117 118static int stk_panel_prepare(struct drm_panel *panel)119{120	struct stk_panel *stk = to_stk_panel(panel);121	int ret;122 123	gpiod_set_value(stk->reset_gpio, 0);124	gpiod_set_value(stk->enable_gpio, 0);125	ret = regulator_enable(stk->supplies[IOVCC].consumer);126	if (ret < 0)127		return ret;128 129	mdelay(8);130	ret = regulator_enable(stk->supplies[POWER].consumer);131	if (ret < 0)132		goto iovccoff;133 134	mdelay(20);135	gpiod_set_value(stk->enable_gpio, 1);136	mdelay(20);137	gpiod_set_value(stk->reset_gpio, 1);138	mdelay(10);139	ret = stk_panel_init(stk);140	if (ret < 0)141		goto poweroff;142 143	ret = stk_panel_on(stk);144	if (ret < 0)145		goto poweroff;146 147	return 0;148 149poweroff:150	regulator_disable(stk->supplies[POWER].consumer);151iovccoff:152	regulator_disable(stk->supplies[IOVCC].consumer);153	gpiod_set_value(stk->reset_gpio, 0);154	gpiod_set_value(stk->enable_gpio, 0);155 156	return ret;157}158 159static const struct drm_display_mode default_mode = {160		.clock = 163204,161		.hdisplay = 1200,162		.hsync_start = 1200 + 144,163		.hsync_end = 1200 + 144 + 16,164		.htotal = 1200 + 144 + 16 + 45,165		.vdisplay = 1920,166		.vsync_start = 1920 + 8,167		.vsync_end = 1920 + 8 + 4,168		.vtotal = 1920 + 8 + 4 + 4,169		.width_mm = 95,170		.height_mm = 151,171};172 173static int stk_panel_get_modes(struct drm_panel *panel,174			       struct drm_connector *connector)175{176	struct drm_display_mode *mode;177 178	mode = drm_mode_duplicate(connector->dev, &default_mode);179	if (!mode) {180		dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",181			default_mode.hdisplay, default_mode.vdisplay,182			drm_mode_vrefresh(&default_mode));183		return -ENOMEM;184	}185 186	drm_mode_set_name(mode);187	drm_mode_probed_add(connector, mode);188	connector->display_info.width_mm = default_mode.width_mm;189	connector->display_info.height_mm = default_mode.height_mm;190	return 1;191}192 193static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)194{195	struct mipi_dsi_device *dsi = bl_get_data(bl);196	int ret;197	u16 brightness;198 199	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;200	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);201	if (ret < 0)202		return ret;203 204	dsi->mode_flags |= MIPI_DSI_MODE_LPM;205	return brightness & 0xff;206}207 208static int dsi_dcs_bl_update_status(struct backlight_device *bl)209{210	struct mipi_dsi_device *dsi = bl_get_data(bl);211	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};212 213	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;214	mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, bl->props.brightness);215	if (dsi_ctx.accum_err)216		return dsi_ctx.accum_err;217 218	dsi->mode_flags |= MIPI_DSI_MODE_LPM;219	return dsi_ctx.accum_err;220}221 222static const struct backlight_ops dsi_bl_ops = {223	.update_status = dsi_dcs_bl_update_status,224	.get_brightness = dsi_dcs_bl_get_brightness,225};226 227static struct backlight_device *228drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)229{230	struct device *dev = &dsi->dev;231	struct backlight_properties props = {232		.type = BACKLIGHT_RAW,233		.brightness = 255,234		.max_brightness = 255,235	};236 237	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,238					      &dsi_bl_ops, &props);239}240 241static const struct drm_panel_funcs stk_panel_funcs = {242	.unprepare = stk_panel_unprepare,243	.prepare = stk_panel_prepare,244	.get_modes = stk_panel_get_modes,245};246 247static const struct of_device_id stk_of_match[] = {248	{ .compatible = "startek,kd070fhfid015", },249	{ }250};251MODULE_DEVICE_TABLE(of, stk_of_match);252 253static int stk_panel_add(struct stk_panel *stk)254{255	struct device *dev = &stk->dsi->dev;256	int ret;257 258	stk->mode = &default_mode;259 260	stk->supplies[IOVCC].supply = "iovcc";261	stk->supplies[POWER].supply = "power";262	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies);263	if (ret) {264		dev_err(dev, "regulator_bulk failed\n");265		return ret;266	}267 268	stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);269	if (IS_ERR(stk->reset_gpio)) {270		ret = PTR_ERR(stk->reset_gpio);271		dev_err(dev, "cannot get reset-gpios %d\n", ret);272		return ret;273	}274 275	stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);276	if (IS_ERR(stk->enable_gpio)) {277		ret = PTR_ERR(stk->enable_gpio);278		dev_err(dev, "cannot get enable-gpio %d\n", ret);279		return ret;280	}281 282	stk->backlight = drm_panel_create_dsi_backlight(stk->dsi);283	if (IS_ERR(stk->backlight)) {284		ret = PTR_ERR(stk->backlight);285		dev_err(dev, "failed to register backlight %d\n", ret);286		return ret;287	}288 289	drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs,290		       DRM_MODE_CONNECTOR_DSI);291 292	drm_panel_add(&stk->base);293 294	return 0;295}296 297static int stk_panel_probe(struct mipi_dsi_device *dsi)298{299	struct stk_panel *stk;300	int ret;301 302	dsi->lanes = 4;303	dsi->format = MIPI_DSI_FMT_RGB888;304	dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM);305 306	stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL);307	if (!stk)308		return -ENOMEM;309 310	mipi_dsi_set_drvdata(dsi, stk);311 312	stk->dsi = dsi;313 314	ret = stk_panel_add(stk);315	if (ret < 0)316		return ret;317 318	ret = mipi_dsi_attach(dsi);319	if (ret < 0)320		drm_panel_remove(&stk->base);321 322	return 0;323}324 325static void stk_panel_remove(struct mipi_dsi_device *dsi)326{327	struct stk_panel *stk = mipi_dsi_get_drvdata(dsi);328	int err;329 330	err = mipi_dsi_detach(dsi);331	if (err < 0)332		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",333			err);334 335	drm_panel_remove(&stk->base);336}337 338static struct mipi_dsi_driver stk_panel_driver = {339	.driver = {340		.name = "panel-startek-kd070fhfid015",341		.of_match_table = stk_of_match,342	},343	.probe = stk_panel_probe,344	.remove = stk_panel_remove,345};346module_mipi_dsi_driver(stk_panel_driver);347 348MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");349MODULE_DESCRIPTION("STARTEK KD070FHFID015");350MODULE_LICENSE("GPL");351