linux/drivers/gpu/drm/bridge/parade-ps8640.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016 MediaTek Inc.
   4 */
   5
   6#include <linux/delay.h>
   7#include <linux/err.h>
   8#include <linux/gpio/consumer.h>
   9#include <linux/i2c.h>
  10#include <linux/module.h>
  11#include <linux/of_graph.h>
  12#include <linux/regulator/consumer.h>
  13
  14#include <drm/drm_bridge.h>
  15#include <drm/drm_mipi_dsi.h>
  16#include <drm/drm_of.h>
  17#include <drm/drm_panel.h>
  18#include <drm/drm_print.h>
  19
  20#define PAGE2_GPIO_H            0xa7
  21#define PS_GPIO9                BIT(1)
  22#define PAGE2_I2C_BYPASS        0xea
  23#define I2C_BYPASS_EN           0xd0
  24#define PAGE2_MCS_EN            0xf3
  25#define MCS_EN                  BIT(0)
  26#define PAGE3_SET_ADD           0xfe
  27#define VDO_CTL_ADD             0x13
  28#define VDO_DIS                 0x18
  29#define VDO_EN                  0x1c
  30#define DP_NUM_LANES            4
  31
  32/*
  33 * PS8640 uses multiple addresses:
  34 * page[0]: for DP control
  35 * page[1]: for VIDEO Bridge
  36 * page[2]: for control top
  37 * page[3]: for DSI Link Control1
  38 * page[4]: for MIPI Phy
  39 * page[5]: for VPLL
  40 * page[6]: for DSI Link Control2
  41 * page[7]: for SPI ROM mapping
  42 */
  43enum page_addr_offset {
  44        PAGE0_DP_CNTL = 0,
  45        PAGE1_VDO_BDG,
  46        PAGE2_TOP_CNTL,
  47        PAGE3_DSI_CNTL1,
  48        PAGE4_MIPI_PHY,
  49        PAGE5_VPLL,
  50        PAGE6_DSI_CNTL2,
  51        PAGE7_SPI_CNTL,
  52        MAX_DEVS
  53};
  54
  55enum ps8640_vdo_control {
  56        DISABLE = VDO_DIS,
  57        ENABLE = VDO_EN,
  58};
  59
  60struct ps8640 {
  61        struct drm_bridge bridge;
  62        struct drm_bridge *panel_bridge;
  63        struct mipi_dsi_device *dsi;
  64        struct i2c_client *page[MAX_DEVS];
  65        struct regulator_bulk_data supplies[2];
  66        struct gpio_desc *gpio_reset;
  67        struct gpio_desc *gpio_powerdown;
  68};
  69
  70static inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e)
  71{
  72        return container_of(e, struct ps8640, bridge);
  73}
  74
  75static int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge,
  76                                     const enum ps8640_vdo_control ctrl)
  77{
  78        struct i2c_client *client = ps_bridge->page[PAGE3_DSI_CNTL1];
  79        u8 vdo_ctrl_buf[] = { VDO_CTL_ADD, ctrl };
  80        int ret;
  81
  82        ret = i2c_smbus_write_i2c_block_data(client, PAGE3_SET_ADD,
  83                                             sizeof(vdo_ctrl_buf),
  84                                             vdo_ctrl_buf);
  85        if (ret < 0)
  86                return ret;
  87
  88        return 0;
  89}
  90
  91static void ps8640_pre_enable(struct drm_bridge *bridge)
  92{
  93        struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
  94        struct i2c_client *client = ps_bridge->page[PAGE2_TOP_CNTL];
  95        unsigned long timeout;
  96        int ret, status;
  97
  98        ret = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
  99                                    ps_bridge->supplies);
 100        if (ret < 0) {
 101                DRM_ERROR("cannot enable regulators %d\n", ret);
 102                return;
 103        }
 104
 105        gpiod_set_value(ps_bridge->gpio_powerdown, 0);
 106        gpiod_set_value(ps_bridge->gpio_reset, 1);
 107        usleep_range(2000, 2500);
 108        gpiod_set_value(ps_bridge->gpio_reset, 0);
 109
 110        /*
 111         * Wait for the ps8640 embedded MCU to be ready
 112         * First wait 200ms and then check the MCU ready flag every 20ms
 113         */
 114        msleep(200);
 115
 116        timeout = jiffies + msecs_to_jiffies(200) + 1;
 117
 118        while (time_is_after_jiffies(timeout)) {
 119                status = i2c_smbus_read_byte_data(client, PAGE2_GPIO_H);
 120                if (status < 0) {
 121                        DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", status);
 122                        goto err_regulators_disable;
 123                }
 124                if ((status & PS_GPIO9) == PS_GPIO9)
 125                        break;
 126
 127                msleep(20);
 128        }
 129
 130        msleep(50);
 131
 132        /*
 133         * The Manufacturer Command Set (MCS) is a device dependent interface
 134         * intended for factory programming of the display module default
 135         * parameters. Once the display module is configured, the MCS shall be
 136         * disabled by the manufacturer. Once disabled, all MCS commands are
 137         * ignored by the display interface.
 138         */
 139        status = i2c_smbus_read_byte_data(client, PAGE2_MCS_EN);
 140        if (status < 0) {
 141                DRM_ERROR("failed read PAGE2_MCS_EN: %d\n", status);
 142                goto err_regulators_disable;
 143        }
 144
 145        ret = i2c_smbus_write_byte_data(client, PAGE2_MCS_EN,
 146                                        status & ~MCS_EN);
 147        if (ret < 0) {
 148                DRM_ERROR("failed write PAGE2_MCS_EN: %d\n", ret);
 149                goto err_regulators_disable;
 150        }
 151
 152        ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE);
 153        if (ret) {
 154                DRM_ERROR("failed to enable VDO: %d\n", ret);
 155                goto err_regulators_disable;
 156        }
 157
 158        /* Switch access edp panel's edid through i2c */
 159        ret = i2c_smbus_write_byte_data(client, PAGE2_I2C_BYPASS,
 160                                        I2C_BYPASS_EN);
 161        if (ret < 0) {
 162                DRM_ERROR("failed write PAGE2_I2C_BYPASS: %d\n", ret);
 163                goto err_regulators_disable;
 164        }
 165
 166        return;
 167
 168err_regulators_disable:
 169        regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
 170                               ps_bridge->supplies);
 171}
 172
 173static void ps8640_post_disable(struct drm_bridge *bridge)
 174{
 175        struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
 176        int ret;
 177
 178        ret = ps8640_bridge_vdo_control(ps_bridge, DISABLE);
 179        if (ret < 0)
 180                DRM_ERROR("failed to disable VDO: %d\n", ret);
 181
 182        gpiod_set_value(ps_bridge->gpio_reset, 1);
 183        gpiod_set_value(ps_bridge->gpio_powerdown, 1);
 184        ret = regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
 185                                     ps_bridge->supplies);
 186        if (ret < 0)
 187                DRM_ERROR("cannot disable regulators %d\n", ret);
 188}
 189
 190static int ps8640_bridge_attach(struct drm_bridge *bridge,
 191                                enum drm_bridge_attach_flags flags)
 192{
 193        struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
 194        struct device *dev = &ps_bridge->page[0]->dev;
 195        struct device_node *in_ep, *dsi_node;
 196        struct mipi_dsi_device *dsi;
 197        struct mipi_dsi_host *host;
 198        int ret;
 199        const struct mipi_dsi_device_info info = { .type = "ps8640",
 200                                                   .channel = 0,
 201                                                   .node = NULL,
 202                                                 };
 203        /* port@0 is ps8640 dsi input port */
 204        in_ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
 205        if (!in_ep)
 206                return -ENODEV;
 207
 208        dsi_node = of_graph_get_remote_port_parent(in_ep);
 209        of_node_put(in_ep);
 210        if (!dsi_node)
 211                return -ENODEV;
 212
 213        host = of_find_mipi_dsi_host_by_node(dsi_node);
 214        of_node_put(dsi_node);
 215        if (!host)
 216                return -ENODEV;
 217
 218        dsi = mipi_dsi_device_register_full(host, &info);
 219        if (IS_ERR(dsi)) {
 220                dev_err(dev, "failed to create dsi device\n");
 221                ret = PTR_ERR(dsi);
 222                return ret;
 223        }
 224
 225        ps_bridge->dsi = dsi;
 226
 227        dsi->host = host;
 228        dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
 229                          MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
 230        dsi->format = MIPI_DSI_FMT_RGB888;
 231        dsi->lanes = DP_NUM_LANES;
 232        ret = mipi_dsi_attach(dsi);
 233        if (ret)
 234                goto err_dsi_attach;
 235
 236        /* Attach the panel-bridge to the dsi bridge */
 237        return drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge,
 238                                 &ps_bridge->bridge, flags);
 239
 240err_dsi_attach:
 241        mipi_dsi_device_unregister(dsi);
 242        return ret;
 243}
 244
 245static const struct drm_bridge_funcs ps8640_bridge_funcs = {
 246        .attach = ps8640_bridge_attach,
 247        .post_disable = ps8640_post_disable,
 248        .pre_enable = ps8640_pre_enable,
 249};
 250
 251static int ps8640_probe(struct i2c_client *client)
 252{
 253        struct device *dev = &client->dev;
 254        struct device_node *np = dev->of_node;
 255        struct ps8640 *ps_bridge;
 256        struct drm_panel *panel;
 257        int ret;
 258        u32 i;
 259
 260        ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
 261        if (!ps_bridge)
 262                return -ENOMEM;
 263
 264        /* port@1 is ps8640 output port */
 265        ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL);
 266        if (ret < 0)
 267                return ret;
 268        if (!panel)
 269                return -ENODEV;
 270
 271        ps_bridge->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
 272        if (IS_ERR(ps_bridge->panel_bridge))
 273                return PTR_ERR(ps_bridge->panel_bridge);
 274
 275        ps_bridge->supplies[0].supply = "vdd33";
 276        ps_bridge->supplies[1].supply = "vdd12";
 277        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies),
 278                                      ps_bridge->supplies);
 279        if (ret)
 280                return ret;
 281
 282        ps_bridge->gpio_powerdown = devm_gpiod_get(&client->dev, "powerdown",
 283                                                   GPIOD_OUT_HIGH);
 284        if (IS_ERR(ps_bridge->gpio_powerdown))
 285                return PTR_ERR(ps_bridge->gpio_powerdown);
 286
 287        /*
 288         * Assert the reset to avoid the bridge being initialized prematurely
 289         */
 290        ps_bridge->gpio_reset = devm_gpiod_get(&client->dev, "reset",
 291                                               GPIOD_OUT_HIGH);
 292        if (IS_ERR(ps_bridge->gpio_reset))
 293                return PTR_ERR(ps_bridge->gpio_reset);
 294
 295        ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
 296        ps_bridge->bridge.of_node = dev->of_node;
 297
 298        ps_bridge->page[PAGE0_DP_CNTL] = client;
 299
 300        for (i = 1; i < ARRAY_SIZE(ps_bridge->page); i++) {
 301                ps_bridge->page[i] = devm_i2c_new_dummy_device(&client->dev,
 302                                                             client->adapter,
 303                                                             client->addr + i);
 304                if (IS_ERR(ps_bridge->page[i])) {
 305                        dev_err(dev, "failed i2c dummy device, address %02x\n",
 306                                client->addr + i);
 307                        return PTR_ERR(ps_bridge->page[i]);
 308                }
 309        }
 310
 311        i2c_set_clientdata(client, ps_bridge);
 312
 313        drm_bridge_add(&ps_bridge->bridge);
 314
 315        return 0;
 316}
 317
 318static int ps8640_remove(struct i2c_client *client)
 319{
 320        struct ps8640 *ps_bridge = i2c_get_clientdata(client);
 321
 322        drm_bridge_remove(&ps_bridge->bridge);
 323
 324        return 0;
 325}
 326
 327static const struct of_device_id ps8640_match[] = {
 328        { .compatible = "parade,ps8640" },
 329        { }
 330};
 331MODULE_DEVICE_TABLE(of, ps8640_match);
 332
 333static struct i2c_driver ps8640_driver = {
 334        .probe_new = ps8640_probe,
 335        .remove = ps8640_remove,
 336        .driver = {
 337                .name = "ps8640",
 338                .of_match_table = ps8640_match,
 339        },
 340};
 341module_i2c_driver(ps8640_driver);
 342
 343MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
 344MODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>");
 345MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>");
 346MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver");
 347MODULE_LICENSE("GPL v2");
 348