162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
462306a36Sopenharmony_ci *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/of.h>
962306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <drm/drm_crtc.h>
1262306a36Sopenharmony_ci#include <drm/drm_device.h>
1362306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h>
1462306a36Sopenharmony_ci#include <drm/drm_panel.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <video/mipi_display.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct osd101t2587_panel {
1962306a36Sopenharmony_ci	struct drm_panel base;
2062306a36Sopenharmony_ci	struct mipi_dsi_device *dsi;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	struct regulator *supply;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	bool prepared;
2562306a36Sopenharmony_ci	bool enabled;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	const struct drm_display_mode *default_mode;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic inline struct osd101t2587_panel *ti_osd_panel(struct drm_panel *panel)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	return container_of(panel, struct osd101t2587_panel, base);
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int osd101t2587_panel_disable(struct drm_panel *panel)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
3862306a36Sopenharmony_ci	int ret;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (!osd101t2587->enabled)
4162306a36Sopenharmony_ci		return 0;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	ret = mipi_dsi_shutdown_peripheral(osd101t2587->dsi);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	osd101t2587->enabled = false;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return ret;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int osd101t2587_panel_unprepare(struct drm_panel *panel)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (!osd101t2587->prepared)
5562306a36Sopenharmony_ci		return 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	regulator_disable(osd101t2587->supply);
5862306a36Sopenharmony_ci	osd101t2587->prepared = false;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return 0;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int osd101t2587_panel_prepare(struct drm_panel *panel)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
6662306a36Sopenharmony_ci	int ret;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (osd101t2587->prepared)
6962306a36Sopenharmony_ci		return 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	ret = regulator_enable(osd101t2587->supply);
7262306a36Sopenharmony_ci	if (!ret)
7362306a36Sopenharmony_ci		osd101t2587->prepared = true;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return ret;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int osd101t2587_panel_enable(struct drm_panel *panel)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
8162306a36Sopenharmony_ci	int ret;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (osd101t2587->enabled)
8462306a36Sopenharmony_ci		return 0;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	ret = mipi_dsi_turn_on_peripheral(osd101t2587->dsi);
8762306a36Sopenharmony_ci	if (ret)
8862306a36Sopenharmony_ci		return ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	osd101t2587->enabled = true;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return ret;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const struct drm_display_mode default_mode_osd101t2587 = {
9662306a36Sopenharmony_ci	.clock = 164400,
9762306a36Sopenharmony_ci	.hdisplay = 1920,
9862306a36Sopenharmony_ci	.hsync_start = 1920 + 152,
9962306a36Sopenharmony_ci	.hsync_end = 1920 + 152 + 52,
10062306a36Sopenharmony_ci	.htotal = 1920 + 152 + 52 + 20,
10162306a36Sopenharmony_ci	.vdisplay = 1200,
10262306a36Sopenharmony_ci	.vsync_start = 1200 + 24,
10362306a36Sopenharmony_ci	.vsync_end = 1200 + 24 + 6,
10462306a36Sopenharmony_ci	.vtotal = 1200 + 24 + 6 + 48,
10562306a36Sopenharmony_ci	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int osd101t2587_panel_get_modes(struct drm_panel *panel,
10962306a36Sopenharmony_ci				       struct drm_connector *connector)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
11262306a36Sopenharmony_ci	struct drm_display_mode *mode;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	mode = drm_mode_duplicate(connector->dev, osd101t2587->default_mode);
11562306a36Sopenharmony_ci	if (!mode) {
11662306a36Sopenharmony_ci		dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
11762306a36Sopenharmony_ci			osd101t2587->default_mode->hdisplay,
11862306a36Sopenharmony_ci			osd101t2587->default_mode->vdisplay,
11962306a36Sopenharmony_ci			drm_mode_vrefresh(osd101t2587->default_mode));
12062306a36Sopenharmony_ci		return -ENOMEM;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	drm_mode_set_name(mode);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	drm_mode_probed_add(connector, mode);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	connector->display_info.width_mm = 217;
12862306a36Sopenharmony_ci	connector->display_info.height_mm = 136;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 1;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic const struct drm_panel_funcs osd101t2587_panel_funcs = {
13462306a36Sopenharmony_ci	.disable = osd101t2587_panel_disable,
13562306a36Sopenharmony_ci	.unprepare = osd101t2587_panel_unprepare,
13662306a36Sopenharmony_ci	.prepare = osd101t2587_panel_prepare,
13762306a36Sopenharmony_ci	.enable = osd101t2587_panel_enable,
13862306a36Sopenharmony_ci	.get_modes = osd101t2587_panel_get_modes,
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic const struct of_device_id osd101t2587_of_match[] = {
14262306a36Sopenharmony_ci	{
14362306a36Sopenharmony_ci		.compatible = "osddisplays,osd101t2587-53ts",
14462306a36Sopenharmony_ci		.data = &default_mode_osd101t2587,
14562306a36Sopenharmony_ci	}, {
14662306a36Sopenharmony_ci		/* sentinel */
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, osd101t2587_of_match);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int osd101t2587_panel_add(struct osd101t2587_panel *osd101t2587)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct device *dev = &osd101t2587->dsi->dev;
15462306a36Sopenharmony_ci	int ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	osd101t2587->supply = devm_regulator_get(dev, "power");
15762306a36Sopenharmony_ci	if (IS_ERR(osd101t2587->supply))
15862306a36Sopenharmony_ci		return PTR_ERR(osd101t2587->supply);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	drm_panel_init(&osd101t2587->base, &osd101t2587->dsi->dev,
16162306a36Sopenharmony_ci		       &osd101t2587_panel_funcs, DRM_MODE_CONNECTOR_DSI);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	ret = drm_panel_of_backlight(&osd101t2587->base);
16462306a36Sopenharmony_ci	if (ret)
16562306a36Sopenharmony_ci		return ret;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	drm_panel_add(&osd101t2587->base);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int osd101t2587_panel_probe(struct mipi_dsi_device *dsi)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct osd101t2587_panel *osd101t2587;
17562306a36Sopenharmony_ci	const struct of_device_id *id;
17662306a36Sopenharmony_ci	int ret;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	id = of_match_node(osd101t2587_of_match, dsi->dev.of_node);
17962306a36Sopenharmony_ci	if (!id)
18062306a36Sopenharmony_ci		return -ENODEV;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	dsi->lanes = 4;
18362306a36Sopenharmony_ci	dsi->format = MIPI_DSI_FMT_RGB888;
18462306a36Sopenharmony_ci	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
18562306a36Sopenharmony_ci			  MIPI_DSI_MODE_VIDEO_BURST |
18662306a36Sopenharmony_ci			  MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
18762306a36Sopenharmony_ci			  MIPI_DSI_MODE_NO_EOT_PACKET;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	osd101t2587 = devm_kzalloc(&dsi->dev, sizeof(*osd101t2587), GFP_KERNEL);
19062306a36Sopenharmony_ci	if (!osd101t2587)
19162306a36Sopenharmony_ci		return -ENOMEM;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	mipi_dsi_set_drvdata(dsi, osd101t2587);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	osd101t2587->dsi = dsi;
19662306a36Sopenharmony_ci	osd101t2587->default_mode = id->data;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	ret = osd101t2587_panel_add(osd101t2587);
19962306a36Sopenharmony_ci	if (ret < 0)
20062306a36Sopenharmony_ci		return ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	ret = mipi_dsi_attach(dsi);
20362306a36Sopenharmony_ci	if (ret)
20462306a36Sopenharmony_ci		drm_panel_remove(&osd101t2587->base);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return ret;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void osd101t2587_panel_remove(struct mipi_dsi_device *dsi)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi);
21262306a36Sopenharmony_ci	int ret;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ret = drm_panel_disable(&osd101t2587->base);
21562306a36Sopenharmony_ci	if (ret < 0)
21662306a36Sopenharmony_ci		dev_warn(&dsi->dev, "failed to disable panel: %d\n", ret);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	drm_panel_unprepare(&osd101t2587->base);
21962306a36Sopenharmony_ci	drm_panel_remove(&osd101t2587->base);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	ret = mipi_dsi_detach(dsi);
22262306a36Sopenharmony_ci	if (ret < 0)
22362306a36Sopenharmony_ci		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void osd101t2587_panel_shutdown(struct mipi_dsi_device *dsi)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	drm_panel_disable(&osd101t2587->base);
23162306a36Sopenharmony_ci	drm_panel_unprepare(&osd101t2587->base);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic struct mipi_dsi_driver osd101t2587_panel_driver = {
23562306a36Sopenharmony_ci	.driver = {
23662306a36Sopenharmony_ci		.name = "panel-osd-osd101t2587-53ts",
23762306a36Sopenharmony_ci		.of_match_table = osd101t2587_of_match,
23862306a36Sopenharmony_ci	},
23962306a36Sopenharmony_ci	.probe = osd101t2587_panel_probe,
24062306a36Sopenharmony_ci	.remove = osd101t2587_panel_remove,
24162306a36Sopenharmony_ci	.shutdown = osd101t2587_panel_shutdown,
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_cimodule_mipi_dsi_driver(osd101t2587_panel_driver);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ciMODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
24662306a36Sopenharmony_ciMODULE_DESCRIPTION("OSD101T2587-53TS DSI panel");
24762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
248