18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016 MediaTek Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/err.h> 88c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 128c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define PAGE2_GPIO_H 0xa7 218c2ecf20Sopenharmony_ci#define PS_GPIO9 BIT(1) 228c2ecf20Sopenharmony_ci#define PAGE2_I2C_BYPASS 0xea 238c2ecf20Sopenharmony_ci#define I2C_BYPASS_EN 0xd0 248c2ecf20Sopenharmony_ci#define PAGE2_MCS_EN 0xf3 258c2ecf20Sopenharmony_ci#define MCS_EN BIT(0) 268c2ecf20Sopenharmony_ci#define PAGE3_SET_ADD 0xfe 278c2ecf20Sopenharmony_ci#define VDO_CTL_ADD 0x13 288c2ecf20Sopenharmony_ci#define VDO_DIS 0x18 298c2ecf20Sopenharmony_ci#define VDO_EN 0x1c 308c2ecf20Sopenharmony_ci#define DP_NUM_LANES 4 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * PS8640 uses multiple addresses: 348c2ecf20Sopenharmony_ci * page[0]: for DP control 358c2ecf20Sopenharmony_ci * page[1]: for VIDEO Bridge 368c2ecf20Sopenharmony_ci * page[2]: for control top 378c2ecf20Sopenharmony_ci * page[3]: for DSI Link Control1 388c2ecf20Sopenharmony_ci * page[4]: for MIPI Phy 398c2ecf20Sopenharmony_ci * page[5]: for VPLL 408c2ecf20Sopenharmony_ci * page[6]: for DSI Link Control2 418c2ecf20Sopenharmony_ci * page[7]: for SPI ROM mapping 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cienum page_addr_offset { 448c2ecf20Sopenharmony_ci PAGE0_DP_CNTL = 0, 458c2ecf20Sopenharmony_ci PAGE1_VDO_BDG, 468c2ecf20Sopenharmony_ci PAGE2_TOP_CNTL, 478c2ecf20Sopenharmony_ci PAGE3_DSI_CNTL1, 488c2ecf20Sopenharmony_ci PAGE4_MIPI_PHY, 498c2ecf20Sopenharmony_ci PAGE5_VPLL, 508c2ecf20Sopenharmony_ci PAGE6_DSI_CNTL2, 518c2ecf20Sopenharmony_ci PAGE7_SPI_CNTL, 528c2ecf20Sopenharmony_ci MAX_DEVS 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cienum ps8640_vdo_control { 568c2ecf20Sopenharmony_ci DISABLE = VDO_DIS, 578c2ecf20Sopenharmony_ci ENABLE = VDO_EN, 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct ps8640 { 618c2ecf20Sopenharmony_ci struct drm_bridge bridge; 628c2ecf20Sopenharmony_ci struct drm_bridge *panel_bridge; 638c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi; 648c2ecf20Sopenharmony_ci struct i2c_client *page[MAX_DEVS]; 658c2ecf20Sopenharmony_ci struct regulator_bulk_data supplies[2]; 668c2ecf20Sopenharmony_ci struct gpio_desc *gpio_reset; 678c2ecf20Sopenharmony_ci struct gpio_desc *gpio_powerdown; 688c2ecf20Sopenharmony_ci bool powered; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci return container_of(e, struct ps8640, bridge); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge, 778c2ecf20Sopenharmony_ci const enum ps8640_vdo_control ctrl) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct i2c_client *client = ps_bridge->page[PAGE3_DSI_CNTL1]; 808c2ecf20Sopenharmony_ci u8 vdo_ctrl_buf[] = { VDO_CTL_ADD, ctrl }; 818c2ecf20Sopenharmony_ci int ret; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci ret = i2c_smbus_write_i2c_block_data(client, PAGE3_SET_ADD, 848c2ecf20Sopenharmony_ci sizeof(vdo_ctrl_buf), 858c2ecf20Sopenharmony_ci vdo_ctrl_buf); 868c2ecf20Sopenharmony_ci if (ret < 0) { 878c2ecf20Sopenharmony_ci DRM_ERROR("failed to %sable VDO: %d\n", 888c2ecf20Sopenharmony_ci ctrl == ENABLE ? "en" : "dis", ret); 898c2ecf20Sopenharmony_ci return ret; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void ps8640_bridge_poweron(struct ps8640 *ps_bridge) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct i2c_client *client = ps_bridge->page[PAGE2_TOP_CNTL]; 988c2ecf20Sopenharmony_ci unsigned long timeout; 998c2ecf20Sopenharmony_ci int ret, status; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (ps_bridge->powered) 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies), 1058c2ecf20Sopenharmony_ci ps_bridge->supplies); 1068c2ecf20Sopenharmony_ci if (ret < 0) { 1078c2ecf20Sopenharmony_ci DRM_ERROR("cannot enable regulators %d\n", ret); 1088c2ecf20Sopenharmony_ci return; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci gpiod_set_value(ps_bridge->gpio_powerdown, 0); 1128c2ecf20Sopenharmony_ci gpiod_set_value(ps_bridge->gpio_reset, 1); 1138c2ecf20Sopenharmony_ci usleep_range(2000, 2500); 1148c2ecf20Sopenharmony_ci gpiod_set_value(ps_bridge->gpio_reset, 0); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * Wait for the ps8640 embedded MCU to be ready 1188c2ecf20Sopenharmony_ci * First wait 200ms and then check the MCU ready flag every 20ms 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci msleep(200); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(200) + 1; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci while (time_is_after_jiffies(timeout)) { 1258c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte_data(client, PAGE2_GPIO_H); 1268c2ecf20Sopenharmony_ci if (status < 0) { 1278c2ecf20Sopenharmony_ci DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", status); 1288c2ecf20Sopenharmony_ci goto err_regulators_disable; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci if ((status & PS_GPIO9) == PS_GPIO9) 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci msleep(20); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci msleep(50); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* 1398c2ecf20Sopenharmony_ci * The Manufacturer Command Set (MCS) is a device dependent interface 1408c2ecf20Sopenharmony_ci * intended for factory programming of the display module default 1418c2ecf20Sopenharmony_ci * parameters. Once the display module is configured, the MCS shall be 1428c2ecf20Sopenharmony_ci * disabled by the manufacturer. Once disabled, all MCS commands are 1438c2ecf20Sopenharmony_ci * ignored by the display interface. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte_data(client, PAGE2_MCS_EN); 1468c2ecf20Sopenharmony_ci if (status < 0) { 1478c2ecf20Sopenharmony_ci DRM_ERROR("failed read PAGE2_MCS_EN: %d\n", status); 1488c2ecf20Sopenharmony_ci goto err_regulators_disable; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PAGE2_MCS_EN, 1528c2ecf20Sopenharmony_ci status & ~MCS_EN); 1538c2ecf20Sopenharmony_ci if (ret < 0) { 1548c2ecf20Sopenharmony_ci DRM_ERROR("failed write PAGE2_MCS_EN: %d\n", ret); 1558c2ecf20Sopenharmony_ci goto err_regulators_disable; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Switch access edp panel's edid through i2c */ 1598c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PAGE2_I2C_BYPASS, 1608c2ecf20Sopenharmony_ci I2C_BYPASS_EN); 1618c2ecf20Sopenharmony_ci if (ret < 0) { 1628c2ecf20Sopenharmony_ci DRM_ERROR("failed write PAGE2_I2C_BYPASS: %d\n", ret); 1638c2ecf20Sopenharmony_ci goto err_regulators_disable; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci ps_bridge->powered = true; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cierr_regulators_disable: 1718c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies), 1728c2ecf20Sopenharmony_ci ps_bridge->supplies); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void ps8640_bridge_poweroff(struct ps8640 *ps_bridge) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!ps_bridge->powered) 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci gpiod_set_value(ps_bridge->gpio_reset, 1); 1838c2ecf20Sopenharmony_ci gpiod_set_value(ps_bridge->gpio_powerdown, 1); 1848c2ecf20Sopenharmony_ci ret = regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies), 1858c2ecf20Sopenharmony_ci ps_bridge->supplies); 1868c2ecf20Sopenharmony_ci if (ret < 0) 1878c2ecf20Sopenharmony_ci DRM_ERROR("cannot disable regulators %d\n", ret); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ps_bridge->powered = false; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void ps8640_pre_enable(struct drm_bridge *bridge) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); 1958c2ecf20Sopenharmony_ci int ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ps8640_bridge_poweron(ps_bridge); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE); 2008c2ecf20Sopenharmony_ci if (ret < 0) 2018c2ecf20Sopenharmony_ci ps8640_bridge_poweroff(ps_bridge); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void ps8640_post_disable(struct drm_bridge *bridge) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ps8640_bridge_vdo_control(ps_bridge, DISABLE); 2098c2ecf20Sopenharmony_ci ps8640_bridge_poweroff(ps_bridge); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int ps8640_bridge_attach(struct drm_bridge *bridge, 2138c2ecf20Sopenharmony_ci enum drm_bridge_attach_flags flags) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); 2168c2ecf20Sopenharmony_ci struct device *dev = &ps_bridge->page[0]->dev; 2178c2ecf20Sopenharmony_ci struct device_node *in_ep, *dsi_node; 2188c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi; 2198c2ecf20Sopenharmony_ci struct mipi_dsi_host *host; 2208c2ecf20Sopenharmony_ci int ret; 2218c2ecf20Sopenharmony_ci const struct mipi_dsi_device_info info = { .type = "ps8640", 2228c2ecf20Sopenharmony_ci .channel = 0, 2238c2ecf20Sopenharmony_ci .node = NULL, 2248c2ecf20Sopenharmony_ci }; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) 2278c2ecf20Sopenharmony_ci return -EINVAL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* port@0 is ps8640 dsi input port */ 2308c2ecf20Sopenharmony_ci in_ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); 2318c2ecf20Sopenharmony_ci if (!in_ep) 2328c2ecf20Sopenharmony_ci return -ENODEV; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci dsi_node = of_graph_get_remote_port_parent(in_ep); 2358c2ecf20Sopenharmony_ci of_node_put(in_ep); 2368c2ecf20Sopenharmony_ci if (!dsi_node) 2378c2ecf20Sopenharmony_ci return -ENODEV; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci host = of_find_mipi_dsi_host_by_node(dsi_node); 2408c2ecf20Sopenharmony_ci of_node_put(dsi_node); 2418c2ecf20Sopenharmony_ci if (!host) 2428c2ecf20Sopenharmony_ci return -ENODEV; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci dsi = mipi_dsi_device_register_full(host, &info); 2458c2ecf20Sopenharmony_ci if (IS_ERR(dsi)) { 2468c2ecf20Sopenharmony_ci dev_err(dev, "failed to create dsi device\n"); 2478c2ecf20Sopenharmony_ci ret = PTR_ERR(dsi); 2488c2ecf20Sopenharmony_ci return ret; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci ps_bridge->dsi = dsi; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci dsi->host = host; 2548c2ecf20Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 2558c2ecf20Sopenharmony_ci MIPI_DSI_MODE_VIDEO_SYNC_PULSE; 2568c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 2578c2ecf20Sopenharmony_ci dsi->lanes = DP_NUM_LANES; 2588c2ecf20Sopenharmony_ci ret = mipi_dsi_attach(dsi); 2598c2ecf20Sopenharmony_ci if (ret) 2608c2ecf20Sopenharmony_ci goto err_dsi_attach; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Attach the panel-bridge to the dsi bridge */ 2638c2ecf20Sopenharmony_ci return drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge, 2648c2ecf20Sopenharmony_ci &ps_bridge->bridge, flags); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cierr_dsi_attach: 2678c2ecf20Sopenharmony_ci mipi_dsi_device_unregister(dsi); 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge, 2728c2ecf20Sopenharmony_ci struct drm_connector *connector) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); 2758c2ecf20Sopenharmony_ci bool poweroff = !ps_bridge->powered; 2768c2ecf20Sopenharmony_ci struct edid *edid; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * When we end calling get_edid() triggered by an ioctl, i.e 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * drm_mode_getconnector (ioctl) 2828c2ecf20Sopenharmony_ci * -> drm_helper_probe_single_connector_modes 2838c2ecf20Sopenharmony_ci * -> drm_bridge_connector_get_modes 2848c2ecf20Sopenharmony_ci * -> ps8640_bridge_get_edid 2858c2ecf20Sopenharmony_ci * 2868c2ecf20Sopenharmony_ci * We need to make sure that what we need is enabled before reading 2878c2ecf20Sopenharmony_ci * EDID, for this chip, we need to do a full poweron, otherwise it will 2888c2ecf20Sopenharmony_ci * fail. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci drm_bridge_chain_pre_enable(bridge); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci edid = drm_get_edid(connector, 2938c2ecf20Sopenharmony_ci ps_bridge->page[PAGE0_DP_CNTL]->adapter); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* 2968c2ecf20Sopenharmony_ci * If we call the get_edid() function without having enabled the chip 2978c2ecf20Sopenharmony_ci * before, return the chip to its original power state. 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci if (poweroff) 3008c2ecf20Sopenharmony_ci drm_bridge_chain_post_disable(bridge); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return edid; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs ps8640_bridge_funcs = { 3068c2ecf20Sopenharmony_ci .attach = ps8640_bridge_attach, 3078c2ecf20Sopenharmony_ci .get_edid = ps8640_bridge_get_edid, 3088c2ecf20Sopenharmony_ci .post_disable = ps8640_post_disable, 3098c2ecf20Sopenharmony_ci .pre_enable = ps8640_pre_enable, 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int ps8640_probe(struct i2c_client *client) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 3158c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 3168c2ecf20Sopenharmony_ci struct ps8640 *ps_bridge; 3178c2ecf20Sopenharmony_ci struct drm_panel *panel; 3188c2ecf20Sopenharmony_ci int ret; 3198c2ecf20Sopenharmony_ci u32 i; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL); 3228c2ecf20Sopenharmony_ci if (!ps_bridge) 3238c2ecf20Sopenharmony_ci return -ENOMEM; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* port@1 is ps8640 output port */ 3268c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL); 3278c2ecf20Sopenharmony_ci if (ret < 0) 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci if (!panel) 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ps_bridge->panel_bridge = devm_drm_panel_bridge_add(dev, panel); 3338c2ecf20Sopenharmony_ci if (IS_ERR(ps_bridge->panel_bridge)) 3348c2ecf20Sopenharmony_ci return PTR_ERR(ps_bridge->panel_bridge); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ps_bridge->supplies[0].supply = "vdd12"; 3378c2ecf20Sopenharmony_ci ps_bridge->supplies[1].supply = "vdd33"; 3388c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies), 3398c2ecf20Sopenharmony_ci ps_bridge->supplies); 3408c2ecf20Sopenharmony_ci if (ret) 3418c2ecf20Sopenharmony_ci return ret; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ps_bridge->gpio_powerdown = devm_gpiod_get(&client->dev, "powerdown", 3448c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 3458c2ecf20Sopenharmony_ci if (IS_ERR(ps_bridge->gpio_powerdown)) 3468c2ecf20Sopenharmony_ci return PTR_ERR(ps_bridge->gpio_powerdown); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* 3498c2ecf20Sopenharmony_ci * Assert the reset to avoid the bridge being initialized prematurely 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci ps_bridge->gpio_reset = devm_gpiod_get(&client->dev, "reset", 3528c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 3538c2ecf20Sopenharmony_ci if (IS_ERR(ps_bridge->gpio_reset)) 3548c2ecf20Sopenharmony_ci return PTR_ERR(ps_bridge->gpio_reset); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ps_bridge->bridge.funcs = &ps8640_bridge_funcs; 3578c2ecf20Sopenharmony_ci ps_bridge->bridge.of_node = dev->of_node; 3588c2ecf20Sopenharmony_ci ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID; 3598c2ecf20Sopenharmony_ci ps_bridge->bridge.type = DRM_MODE_CONNECTOR_eDP; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci ps_bridge->page[PAGE0_DP_CNTL] = client; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(ps_bridge->page); i++) { 3648c2ecf20Sopenharmony_ci ps_bridge->page[i] = devm_i2c_new_dummy_device(&client->dev, 3658c2ecf20Sopenharmony_ci client->adapter, 3668c2ecf20Sopenharmony_ci client->addr + i); 3678c2ecf20Sopenharmony_ci if (IS_ERR(ps_bridge->page[i])) { 3688c2ecf20Sopenharmony_ci dev_err(dev, "failed i2c dummy device, address %02x\n", 3698c2ecf20Sopenharmony_ci client->addr + i); 3708c2ecf20Sopenharmony_ci return PTR_ERR(ps_bridge->page[i]); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci i2c_set_clientdata(client, ps_bridge); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci drm_bridge_add(&ps_bridge->bridge); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int ps8640_remove(struct i2c_client *client) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct ps8640 *ps_bridge = i2c_get_clientdata(client); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci drm_bridge_remove(&ps_bridge->bridge); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic const struct of_device_id ps8640_match[] = { 3918c2ecf20Sopenharmony_ci { .compatible = "parade,ps8640" }, 3928c2ecf20Sopenharmony_ci { } 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ps8640_match); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic struct i2c_driver ps8640_driver = { 3978c2ecf20Sopenharmony_ci .probe_new = ps8640_probe, 3988c2ecf20Sopenharmony_ci .remove = ps8640_remove, 3998c2ecf20Sopenharmony_ci .driver = { 4008c2ecf20Sopenharmony_ci .name = "ps8640", 4018c2ecf20Sopenharmony_ci .of_match_table = ps8640_match, 4028c2ecf20Sopenharmony_ci }, 4038c2ecf20Sopenharmony_ci}; 4048c2ecf20Sopenharmony_cimodule_i2c_driver(ps8640_driver); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>"); 4078c2ecf20Sopenharmony_ciMODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>"); 4088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>"); 4098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver"); 4108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 411