linux/drivers/gpu/drm/bridge/tc358762.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2020 Marek Vasut <marex@denx.de>
   4 *
   5 * Based on tc358764.c by
   6 *  Andrzej Hajda <a.hajda@samsung.com>
   7 *  Maciej Purski <m.purski@samsung.com>
   8 *
   9 * Based on rpi_touchscreen.c by
  10 *  Eric Anholt <eric@anholt.net>
  11 */
  12
  13#include <linux/delay.h>
  14#include <linux/module.h>
  15#include <linux/of_graph.h>
  16#include <linux/regulator/consumer.h>
  17
  18#include <video/mipi_display.h>
  19
  20#include <drm/drm_atomic_helper.h>
  21#include <drm/drm_crtc.h>
  22#include <drm/drm_fb_helper.h>
  23#include <drm/drm_mipi_dsi.h>
  24#include <drm/drm_of.h>
  25#include <drm/drm_panel.h>
  26#include <drm/drm_print.h>
  27#include <drm/drm_probe_helper.h>
  28
  29/* PPI layer registers */
  30#define PPI_STARTPPI            0x0104 /* START control bit */
  31#define PPI_LPTXTIMECNT         0x0114 /* LPTX timing signal */
  32#define PPI_D0S_ATMR            0x0144
  33#define PPI_D1S_ATMR            0x0148
  34#define PPI_D0S_CLRSIPOCOUNT    0x0164 /* Assertion timer for Lane 0 */
  35#define PPI_D1S_CLRSIPOCOUNT    0x0168 /* Assertion timer for Lane 1 */
  36#define PPI_START_FUNCTION      1
  37
  38/* DSI layer registers */
  39#define DSI_STARTDSI            0x0204 /* START control bit of DSI-TX */
  40#define DSI_LANEENABLE          0x0210 /* Enables each lane */
  41#define DSI_RX_START            1
  42
  43/* LCDC/DPI Host Registers */
  44#define LCDCTRL                 0x0420
  45
  46/* SPI Master Registers */
  47#define SPICMR                  0x0450
  48#define SPITCR                  0x0454
  49
  50/* System Controller Registers */
  51#define SYSCTRL                 0x0464
  52
  53/* System registers */
  54#define LPX_PERIOD              3
  55
  56/* Lane enable PPI and DSI register bits */
  57#define LANEENABLE_CLEN         BIT(0)
  58#define LANEENABLE_L0EN         BIT(1)
  59#define LANEENABLE_L1EN         BIT(2)
  60
  61struct tc358762 {
  62        struct device *dev;
  63        struct drm_bridge bridge;
  64        struct drm_connector connector;
  65        struct regulator *regulator;
  66        struct drm_bridge *panel_bridge;
  67        bool pre_enabled;
  68        int error;
  69};
  70
  71static int tc358762_clear_error(struct tc358762 *ctx)
  72{
  73        int ret = ctx->error;
  74
  75        ctx->error = 0;
  76        return ret;
  77}
  78
  79static void tc358762_write(struct tc358762 *ctx, u16 addr, u32 val)
  80{
  81        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
  82        ssize_t ret;
  83        u8 data[6];
  84
  85        if (ctx->error)
  86                return;
  87
  88        data[0] = addr;
  89        data[1] = addr >> 8;
  90        data[2] = val;
  91        data[3] = val >> 8;
  92        data[4] = val >> 16;
  93        data[5] = val >> 24;
  94
  95        ret = mipi_dsi_generic_write(dsi, data, sizeof(data));
  96        if (ret < 0)
  97                ctx->error = ret;
  98}
  99
 100static inline struct tc358762 *bridge_to_tc358762(struct drm_bridge *bridge)
 101{
 102        return container_of(bridge, struct tc358762, bridge);
 103}
 104
 105static int tc358762_init(struct tc358762 *ctx)
 106{
 107        tc358762_write(ctx, DSI_LANEENABLE,
 108                       LANEENABLE_L0EN | LANEENABLE_CLEN);
 109        tc358762_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5);
 110        tc358762_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5);
 111        tc358762_write(ctx, PPI_D0S_ATMR, 0);
 112        tc358762_write(ctx, PPI_D1S_ATMR, 0);
 113        tc358762_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD);
 114
 115        tc358762_write(ctx, SPICMR, 0x00);
 116        tc358762_write(ctx, LCDCTRL, 0x00100150);
 117        tc358762_write(ctx, SYSCTRL, 0x040f);
 118        msleep(100);
 119
 120        tc358762_write(ctx, PPI_STARTPPI, PPI_START_FUNCTION);
 121        tc358762_write(ctx, DSI_STARTDSI, DSI_RX_START);
 122
 123        msleep(100);
 124
 125        return tc358762_clear_error(ctx);
 126}
 127
 128static void tc358762_post_disable(struct drm_bridge *bridge)
 129{
 130        struct tc358762 *ctx = bridge_to_tc358762(bridge);
 131        int ret;
 132
 133        /*
 134         * The post_disable hook might be called multiple times.
 135         * We want to avoid regulator imbalance below.
 136         */
 137        if (!ctx->pre_enabled)
 138                return;
 139
 140        ctx->pre_enabled = false;
 141
 142        ret = regulator_disable(ctx->regulator);
 143        if (ret < 0)
 144                dev_err(ctx->dev, "error disabling regulators (%d)\n", ret);
 145}
 146
 147static void tc358762_pre_enable(struct drm_bridge *bridge)
 148{
 149        struct tc358762 *ctx = bridge_to_tc358762(bridge);
 150        int ret;
 151
 152        ret = regulator_enable(ctx->regulator);
 153        if (ret < 0)
 154                dev_err(ctx->dev, "error enabling regulators (%d)\n", ret);
 155
 156        ret = tc358762_init(ctx);
 157        if (ret < 0)
 158                dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
 159
 160        ctx->pre_enabled = true;
 161}
 162
 163static int tc358762_attach(struct drm_bridge *bridge,
 164                           enum drm_bridge_attach_flags flags)
 165{
 166        struct tc358762 *ctx = bridge_to_tc358762(bridge);
 167
 168        return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
 169                                 bridge, flags);
 170}
 171
 172static const struct drm_bridge_funcs tc358762_bridge_funcs = {
 173        .post_disable = tc358762_post_disable,
 174        .pre_enable = tc358762_pre_enable,
 175        .attach = tc358762_attach,
 176};
 177
 178static int tc358762_parse_dt(struct tc358762 *ctx)
 179{
 180        struct drm_bridge *panel_bridge;
 181        struct device *dev = ctx->dev;
 182        struct drm_panel *panel;
 183        int ret;
 184
 185        ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
 186        if (ret)
 187                return ret;
 188
 189        panel_bridge = devm_drm_panel_bridge_add(dev, panel);
 190
 191        if (IS_ERR(panel_bridge))
 192                return PTR_ERR(panel_bridge);
 193
 194        ctx->panel_bridge = panel_bridge;
 195
 196        return 0;
 197}
 198
 199static int tc358762_configure_regulators(struct tc358762 *ctx)
 200{
 201        ctx->regulator = devm_regulator_get(ctx->dev, "vddc");
 202        if (IS_ERR(ctx->regulator))
 203                return PTR_ERR(ctx->regulator);
 204
 205        return 0;
 206}
 207
 208static int tc358762_probe(struct mipi_dsi_device *dsi)
 209{
 210        struct device *dev = &dsi->dev;
 211        struct tc358762 *ctx;
 212        int ret;
 213
 214        ctx = devm_kzalloc(dev, sizeof(struct tc358762), GFP_KERNEL);
 215        if (!ctx)
 216                return -ENOMEM;
 217
 218        mipi_dsi_set_drvdata(dsi, ctx);
 219
 220        ctx->dev = dev;
 221        ctx->pre_enabled = false;
 222
 223        /* TODO: Find out how to get dual-lane mode working */
 224        dsi->lanes = 1;
 225        dsi->format = MIPI_DSI_FMT_RGB888;
 226        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
 227                          MIPI_DSI_MODE_LPM;
 228
 229        ret = tc358762_parse_dt(ctx);
 230        if (ret < 0)
 231                return ret;
 232
 233        ret = tc358762_configure_regulators(ctx);
 234        if (ret < 0)
 235                return ret;
 236
 237        ctx->bridge.funcs = &tc358762_bridge_funcs;
 238        ctx->bridge.type = DRM_MODE_CONNECTOR_DPI;
 239        ctx->bridge.of_node = dev->of_node;
 240
 241        drm_bridge_add(&ctx->bridge);
 242
 243        ret = mipi_dsi_attach(dsi);
 244        if (ret < 0) {
 245                drm_bridge_remove(&ctx->bridge);
 246                dev_err(dev, "failed to attach dsi\n");
 247        }
 248
 249        return ret;
 250}
 251
 252static int tc358762_remove(struct mipi_dsi_device *dsi)
 253{
 254        struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi);
 255
 256        mipi_dsi_detach(dsi);
 257        drm_bridge_remove(&ctx->bridge);
 258
 259        return 0;
 260}
 261
 262static const struct of_device_id tc358762_of_match[] = {
 263        { .compatible = "toshiba,tc358762" },
 264        { }
 265};
 266MODULE_DEVICE_TABLE(of, tc358762_of_match);
 267
 268static struct mipi_dsi_driver tc358762_driver = {
 269        .probe = tc358762_probe,
 270        .remove = tc358762_remove,
 271        .driver = {
 272                .name = "tc358762",
 273                .of_match_table = tc358762_of_match,
 274        },
 275};
 276module_mipi_dsi_driver(tc358762_driver);
 277
 278MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 279MODULE_DESCRIPTION("MIPI-DSI based Driver for TC358762 DSI/DPI Bridge");
 280MODULE_LICENSE("GPL v2");
 281