123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2020 Marek Vasut <[email protected]>
- *
- * Based on tc358764.c by
- * Andrzej Hajda <[email protected]>
- * Maciej Purski <[email protected]>
- *
- * Based on rpi_touchscreen.c by
- * Eric Anholt <[email protected]>
- */
- #include <linux/delay.h>
- #include <linux/module.h>
- #include <linux/of_graph.h>
- #include <linux/regulator/consumer.h>
- #include <video/mipi_display.h>
- #include <drm/drm_atomic_helper.h>
- #include <drm/drm_crtc.h>
- #include <drm/drm_fb_helper.h>
- #include <drm/drm_mipi_dsi.h>
- #include <drm/drm_of.h>
- #include <drm/drm_panel.h>
- #include <drm/drm_print.h>
- #include <drm/drm_probe_helper.h>
- /* PPI layer registers */
- #define PPI_STARTPPI 0x0104 /* START control bit */
- #define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */
- #define PPI_D0S_ATMR 0x0144
- #define PPI_D1S_ATMR 0x0148
- #define PPI_D0S_CLRSIPOCOUNT 0x0164 /* Assertion timer for Lane 0 */
- #define PPI_D1S_CLRSIPOCOUNT 0x0168 /* Assertion timer for Lane 1 */
- #define PPI_START_FUNCTION 1
- /* DSI layer registers */
- #define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */
- #define DSI_LANEENABLE 0x0210 /* Enables each lane */
- #define DSI_RX_START 1
- /* LCDC/DPI Host Registers */
- #define LCDCTRL 0x0420
- /* SPI Master Registers */
- #define SPICMR 0x0450
- #define SPITCR 0x0454
- /* System Controller Registers */
- #define SYSCTRL 0x0464
- /* System registers */
- #define LPX_PERIOD 3
- /* Lane enable PPI and DSI register bits */
- #define LANEENABLE_CLEN BIT(0)
- #define LANEENABLE_L0EN BIT(1)
- #define LANEENABLE_L1EN BIT(2)
- struct tc358762 {
- struct device *dev;
- struct drm_bridge bridge;
- struct regulator *regulator;
- struct drm_bridge *panel_bridge;
- bool pre_enabled;
- int error;
- };
- static int tc358762_clear_error(struct tc358762 *ctx)
- {
- int ret = ctx->error;
- ctx->error = 0;
- return ret;
- }
- static void tc358762_write(struct tc358762 *ctx, u16 addr, u32 val)
- {
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- ssize_t ret;
- u8 data[6];
- if (ctx->error)
- return;
- data[0] = addr;
- data[1] = addr >> 8;
- data[2] = val;
- data[3] = val >> 8;
- data[4] = val >> 16;
- data[5] = val >> 24;
- ret = mipi_dsi_generic_write(dsi, data, sizeof(data));
- if (ret < 0)
- ctx->error = ret;
- }
- static inline struct tc358762 *bridge_to_tc358762(struct drm_bridge *bridge)
- {
- return container_of(bridge, struct tc358762, bridge);
- }
- static int tc358762_init(struct tc358762 *ctx)
- {
- tc358762_write(ctx, DSI_LANEENABLE,
- LANEENABLE_L0EN | LANEENABLE_CLEN);
- tc358762_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5);
- tc358762_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5);
- tc358762_write(ctx, PPI_D0S_ATMR, 0);
- tc358762_write(ctx, PPI_D1S_ATMR, 0);
- tc358762_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD);
- tc358762_write(ctx, SPICMR, 0x00);
- tc358762_write(ctx, LCDCTRL, 0x00100150);
- tc358762_write(ctx, SYSCTRL, 0x040f);
- msleep(100);
- tc358762_write(ctx, PPI_STARTPPI, PPI_START_FUNCTION);
- tc358762_write(ctx, DSI_STARTDSI, DSI_RX_START);
- msleep(100);
- return tc358762_clear_error(ctx);
- }
- static void tc358762_post_disable(struct drm_bridge *bridge)
- {
- struct tc358762 *ctx = bridge_to_tc358762(bridge);
- int ret;
- /*
- * The post_disable hook might be called multiple times.
- * We want to avoid regulator imbalance below.
- */
- if (!ctx->pre_enabled)
- return;
- ctx->pre_enabled = false;
- ret = regulator_disable(ctx->regulator);
- if (ret < 0)
- dev_err(ctx->dev, "error disabling regulators (%d)\n", ret);
- }
- static void tc358762_pre_enable(struct drm_bridge *bridge)
- {
- struct tc358762 *ctx = bridge_to_tc358762(bridge);
- int ret;
- ret = regulator_enable(ctx->regulator);
- if (ret < 0)
- dev_err(ctx->dev, "error enabling regulators (%d)\n", ret);
- ret = tc358762_init(ctx);
- if (ret < 0)
- dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
- ctx->pre_enabled = true;
- }
- static int tc358762_attach(struct drm_bridge *bridge,
- enum drm_bridge_attach_flags flags)
- {
- struct tc358762 *ctx = bridge_to_tc358762(bridge);
- return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
- bridge, flags);
- }
- static const struct drm_bridge_funcs tc358762_bridge_funcs = {
- .post_disable = tc358762_post_disable,
- .pre_enable = tc358762_pre_enable,
- .attach = tc358762_attach,
- };
- static int tc358762_parse_dt(struct tc358762 *ctx)
- {
- struct drm_bridge *panel_bridge;
- struct device *dev = ctx->dev;
- panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
- if (IS_ERR(panel_bridge))
- return PTR_ERR(panel_bridge);
- ctx->panel_bridge = panel_bridge;
- return 0;
- }
- static int tc358762_configure_regulators(struct tc358762 *ctx)
- {
- ctx->regulator = devm_regulator_get(ctx->dev, "vddc");
- if (IS_ERR(ctx->regulator))
- return PTR_ERR(ctx->regulator);
- return 0;
- }
- static int tc358762_probe(struct mipi_dsi_device *dsi)
- {
- struct device *dev = &dsi->dev;
- struct tc358762 *ctx;
- int ret;
- ctx = devm_kzalloc(dev, sizeof(struct tc358762), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
- mipi_dsi_set_drvdata(dsi, ctx);
- ctx->dev = dev;
- ctx->pre_enabled = false;
- /* TODO: Find out how to get dual-lane mode working */
- dsi->lanes = 1;
- dsi->format = MIPI_DSI_FMT_RGB888;
- dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
- MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO_HSE;
- ret = tc358762_parse_dt(ctx);
- if (ret < 0)
- return ret;
- ret = tc358762_configure_regulators(ctx);
- if (ret < 0)
- return ret;
- ctx->bridge.funcs = &tc358762_bridge_funcs;
- ctx->bridge.type = DRM_MODE_CONNECTOR_DPI;
- ctx->bridge.of_node = dev->of_node;
- drm_bridge_add(&ctx->bridge);
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- drm_bridge_remove(&ctx->bridge);
- dev_err(dev, "failed to attach dsi\n");
- }
- return ret;
- }
- static void tc358762_remove(struct mipi_dsi_device *dsi)
- {
- struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi);
- mipi_dsi_detach(dsi);
- drm_bridge_remove(&ctx->bridge);
- }
- static const struct of_device_id tc358762_of_match[] = {
- { .compatible = "toshiba,tc358762" },
- { }
- };
- MODULE_DEVICE_TABLE(of, tc358762_of_match);
- static struct mipi_dsi_driver tc358762_driver = {
- .probe = tc358762_probe,
- .remove = tc358762_remove,
- .driver = {
- .name = "tc358762",
- .of_match_table = tc358762_of_match,
- },
- };
- module_mipi_dsi_driver(tc358762_driver);
- MODULE_AUTHOR("Marek Vasut <[email protected]>");
- MODULE_DESCRIPTION("MIPI-DSI based Driver for TC358762 DSI/DPI Bridge");
- MODULE_LICENSE("GPL v2");
|