18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Parade PS8622 eDP/LVDS bridge driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Google, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/backlight.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_device.h> 168c2ecf20Sopenharmony_ci#include <linux/pm.h> 178c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Brightness scale on the Parade chip */ 288c2ecf20Sopenharmony_ci#define PS8622_MAX_BRIGHTNESS 0xff 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */ 318c2ecf20Sopenharmony_ci#define PS8622_POWER_RISE_T1_MIN_US 10 328c2ecf20Sopenharmony_ci#define PS8622_POWER_RISE_T1_MAX_US 10000 338c2ecf20Sopenharmony_ci#define PS8622_RST_HIGH_T2_MIN_US 3000 348c2ecf20Sopenharmony_ci#define PS8622_RST_HIGH_T2_MAX_US 30000 358c2ecf20Sopenharmony_ci#define PS8622_PWMO_END_T12_MS 200 368c2ecf20Sopenharmony_ci#define PS8622_POWER_FALL_T16_MAX_US 10000 378c2ecf20Sopenharmony_ci#define PS8622_POWER_OFF_T17_MS 500 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \ 408c2ecf20Sopenharmony_ci (PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US)) 418c2ecf20Sopenharmony_ci#error "T2.min + T1.max must be less than T2.max + T1.min" 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct ps8622_bridge { 458c2ecf20Sopenharmony_ci struct i2c_client *client; 468c2ecf20Sopenharmony_ci struct drm_bridge bridge; 478c2ecf20Sopenharmony_ci struct drm_bridge *panel_bridge; 488c2ecf20Sopenharmony_ci struct regulator *v12; 498c2ecf20Sopenharmony_ci struct backlight_device *bl; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci struct gpio_desc *gpio_slp; 528c2ecf20Sopenharmony_ci struct gpio_desc *gpio_rst; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci u32 max_lane_count; 558c2ecf20Sopenharmony_ci u32 lane_count; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci bool enabled; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline struct ps8622_bridge * 618c2ecf20Sopenharmony_ci bridge_to_ps8622(struct drm_bridge *bridge) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return container_of(bridge, struct ps8622_bridge, bridge); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci struct i2c_adapter *adap = client->adapter; 708c2ecf20Sopenharmony_ci struct i2c_msg msg; 718c2ecf20Sopenharmony_ci u8 data[] = {reg, val}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci msg.addr = client->addr + page; 748c2ecf20Sopenharmony_ci msg.flags = 0; 758c2ecf20Sopenharmony_ci msg.len = sizeof(data); 768c2ecf20Sopenharmony_ci msg.buf = data; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = i2c_transfer(adap, &msg, 1); 798c2ecf20Sopenharmony_ci if (ret != 1) 808c2ecf20Sopenharmony_ci pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n", 818c2ecf20Sopenharmony_ci client->addr + page, reg, val, ret); 828c2ecf20Sopenharmony_ci return !(ret == 1); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int ps8622_send_config(struct ps8622_bridge *ps8622) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct i2c_client *cl = ps8622->client; 888c2ecf20Sopenharmony_ci int err = 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* HPD low */ 918c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x02, 0xa1, 0x01); 928c2ecf20Sopenharmony_ci if (err) 938c2ecf20Sopenharmony_ci goto error; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* SW setting: [1:0] SW output 1.2V voltage is lower to 96% */ 968c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x14, 0x01); 978c2ecf20Sopenharmony_ci if (err) 988c2ecf20Sopenharmony_ci goto error; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* RCO SS setting: [5:4] = b01 0.5%, b10 1%, b11 1.5% */ 1018c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0xe3, 0x20); 1028c2ecf20Sopenharmony_ci if (err) 1038c2ecf20Sopenharmony_ci goto error; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* [7] RCO SS enable */ 1068c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0xe2, 0x80); 1078c2ecf20Sopenharmony_ci if (err) 1088c2ecf20Sopenharmony_ci goto error; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* RPHY Setting 1118c2ecf20Sopenharmony_ci * [3:2] CDR tune wait cycle before measure for fine tune 1128c2ecf20Sopenharmony_ci * b00: 1us b01: 0.5us b10:2us, b11: 4us 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x8a, 0x0c); 1158c2ecf20Sopenharmony_ci if (err) 1168c2ecf20Sopenharmony_ci goto error; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* [3] RFD always on */ 1198c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x89, 0x08); 1208c2ecf20Sopenharmony_ci if (err) 1218c2ecf20Sopenharmony_ci goto error; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* CTN lock in/out: 20000ppm/80000ppm. Lock out 2 times. */ 1248c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x71, 0x2d); 1258c2ecf20Sopenharmony_ci if (err) 1268c2ecf20Sopenharmony_ci goto error; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 2.7G CDR settings: NOF=40LSB for HBR CDR setting */ 1298c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x7d, 0x07); 1308c2ecf20Sopenharmony_ci if (err) 1318c2ecf20Sopenharmony_ci goto error; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* [1:0] Fmin=+4bands */ 1348c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x7b, 0x00); 1358c2ecf20Sopenharmony_ci if (err) 1368c2ecf20Sopenharmony_ci goto error; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* [7:5] DCO_FTRNG=+-40% */ 1398c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x7a, 0xfd); 1408c2ecf20Sopenharmony_ci if (err) 1418c2ecf20Sopenharmony_ci goto error; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* 1.62G CDR settings: [5:2]NOF=64LSB [1:0]DCO scale is 2/5 */ 1448c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0xc0, 0x12); 1458c2ecf20Sopenharmony_ci if (err) 1468c2ecf20Sopenharmony_ci goto error; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Gitune=-37% */ 1498c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0xc1, 0x92); 1508c2ecf20Sopenharmony_ci if (err) 1518c2ecf20Sopenharmony_ci goto error; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Fbstep=100% */ 1548c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0xc2, 0x1c); 1558c2ecf20Sopenharmony_ci if (err) 1568c2ecf20Sopenharmony_ci goto error; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* [7] LOS signal disable */ 1598c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x32, 0x80); 1608c2ecf20Sopenharmony_ci if (err) 1618c2ecf20Sopenharmony_ci goto error; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* RPIO Setting: [7:4] LVDS driver bias current : 75% (250mV swing) */ 1648c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x00, 0xb0); 1658c2ecf20Sopenharmony_ci if (err) 1668c2ecf20Sopenharmony_ci goto error; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* [7:6] Right-bar GPIO output strength is 8mA */ 1698c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x15, 0x40); 1708c2ecf20Sopenharmony_ci if (err) 1718c2ecf20Sopenharmony_ci goto error; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* EQ Training State Machine Setting, RCO calibration start */ 1748c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x54, 0x10); 1758c2ecf20Sopenharmony_ci if (err) 1768c2ecf20Sopenharmony_ci goto error; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Logic, needs more than 10 I2C command */ 1798c2ecf20Sopenharmony_ci /* [4:0] MAX_LANE_COUNT set to max supported lanes */ 1808c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0x02, 0x80 | ps8622->max_lane_count); 1818c2ecf20Sopenharmony_ci if (err) 1828c2ecf20Sopenharmony_ci goto error; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* [4:0] LANE_COUNT_SET set to chosen lane count */ 1858c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0x21, 0x80 | ps8622->lane_count); 1868c2ecf20Sopenharmony_ci if (err) 1878c2ecf20Sopenharmony_ci goto error; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x00, 0x52, 0x20); 1908c2ecf20Sopenharmony_ci if (err) 1918c2ecf20Sopenharmony_ci goto error; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* HPD CP toggle enable */ 1948c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x00, 0xf1, 0x03); 1958c2ecf20Sopenharmony_ci if (err) 1968c2ecf20Sopenharmony_ci goto error; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x00, 0x62, 0x41); 1998c2ecf20Sopenharmony_ci if (err) 2008c2ecf20Sopenharmony_ci goto error; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Counter number, add 1ms counter delay */ 2038c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x00, 0xf6, 0x01); 2048c2ecf20Sopenharmony_ci if (err) 2058c2ecf20Sopenharmony_ci goto error; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* [6]PWM function control by DPCD0040f[7], default is PWM block */ 2088c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x00, 0x77, 0x06); 2098c2ecf20Sopenharmony_ci if (err) 2108c2ecf20Sopenharmony_ci goto error; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 04h Adjust VTotal toleranceto fix the 30Hz no display issue */ 2138c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x00, 0x4c, 0x04); 2148c2ecf20Sopenharmony_ci if (err) 2158c2ecf20Sopenharmony_ci goto error; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* DPCD00400='h00, Parade OUI ='h001cf8 */ 2188c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc0, 0x00); 2198c2ecf20Sopenharmony_ci if (err) 2208c2ecf20Sopenharmony_ci goto error; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* DPCD00401='h1c */ 2238c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc1, 0x1c); 2248c2ecf20Sopenharmony_ci if (err) 2258c2ecf20Sopenharmony_ci goto error; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* DPCD00402='hf8 */ 2288c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc2, 0xf8); 2298c2ecf20Sopenharmony_ci if (err) 2308c2ecf20Sopenharmony_ci goto error; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* DPCD403~408 = ASCII code, D2SLV5='h4432534c5635 */ 2338c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc3, 0x44); 2348c2ecf20Sopenharmony_ci if (err) 2358c2ecf20Sopenharmony_ci goto error; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* DPCD404 */ 2388c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc4, 0x32); 2398c2ecf20Sopenharmony_ci if (err) 2408c2ecf20Sopenharmony_ci goto error; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* DPCD405 */ 2438c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc5, 0x53); 2448c2ecf20Sopenharmony_ci if (err) 2458c2ecf20Sopenharmony_ci goto error; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* DPCD406 */ 2488c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc6, 0x4c); 2498c2ecf20Sopenharmony_ci if (err) 2508c2ecf20Sopenharmony_ci goto error; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* DPCD407 */ 2538c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc7, 0x56); 2548c2ecf20Sopenharmony_ci if (err) 2558c2ecf20Sopenharmony_ci goto error; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* DPCD408 */ 2588c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xc8, 0x35); 2598c2ecf20Sopenharmony_ci if (err) 2608c2ecf20Sopenharmony_ci goto error; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* DPCD40A, Initial Code major revision '01' */ 2638c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xca, 0x01); 2648c2ecf20Sopenharmony_ci if (err) 2658c2ecf20Sopenharmony_ci goto error; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* DPCD40B, Initial Code minor revision '05' */ 2688c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xcb, 0x05); 2698c2ecf20Sopenharmony_ci if (err) 2708c2ecf20Sopenharmony_ci goto error; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (ps8622->bl) { 2748c2ecf20Sopenharmony_ci /* DPCD720, internal PWM */ 2758c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xa5, 0xa0); 2768c2ecf20Sopenharmony_ci if (err) 2778c2ecf20Sopenharmony_ci goto error; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* FFh for 100% brightness, 0h for 0% brightness */ 2808c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xa7, 2818c2ecf20Sopenharmony_ci ps8622->bl->props.brightness); 2828c2ecf20Sopenharmony_ci if (err) 2838c2ecf20Sopenharmony_ci goto error; 2848c2ecf20Sopenharmony_ci } else { 2858c2ecf20Sopenharmony_ci /* DPCD720, external PWM */ 2868c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xa5, 0x80); 2878c2ecf20Sopenharmony_ci if (err) 2888c2ecf20Sopenharmony_ci goto error; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Set LVDS output as 6bit-VESA mapping, single LVDS channel */ 2928c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x01, 0xcc, 0x13); 2938c2ecf20Sopenharmony_ci if (err) 2948c2ecf20Sopenharmony_ci goto error; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Enable SSC set by register */ 2978c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x02, 0xb1, 0x20); 2988c2ecf20Sopenharmony_ci if (err) 2998c2ecf20Sopenharmony_ci goto error; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Set SSC enabled and +/-1% central spreading */ 3028c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x10, 0x16); 3038c2ecf20Sopenharmony_ci if (err) 3048c2ecf20Sopenharmony_ci goto error; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Logic end */ 3078c2ecf20Sopenharmony_ci /* MPU Clock source: LC => RCO */ 3088c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x59, 0x60); 3098c2ecf20Sopenharmony_ci if (err) 3108c2ecf20Sopenharmony_ci goto error; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* LC -> RCO */ 3138c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x04, 0x54, 0x14); 3148c2ecf20Sopenharmony_ci if (err) 3158c2ecf20Sopenharmony_ci goto error; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* HPD high */ 3188c2ecf20Sopenharmony_ci err = ps8622_set(cl, 0x02, 0xa1, 0x91); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cierror: 3218c2ecf20Sopenharmony_ci return err ? -EIO : 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int ps8622_backlight_update(struct backlight_device *bl) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct ps8622_bridge *ps8622 = dev_get_drvdata(&bl->dev); 3278c2ecf20Sopenharmony_ci int ret, brightness = bl->props.brightness; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (bl->props.power != FB_BLANK_UNBLANK || 3308c2ecf20Sopenharmony_ci bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) 3318c2ecf20Sopenharmony_ci brightness = 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (!ps8622->enabled) 3348c2ecf20Sopenharmony_ci return -EINVAL; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ret = ps8622_set(ps8622->client, 0x01, 0xa7, brightness); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic const struct backlight_ops ps8622_backlight_ops = { 3428c2ecf20Sopenharmony_ci .update_status = ps8622_backlight_update, 3438c2ecf20Sopenharmony_ci}; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void ps8622_pre_enable(struct drm_bridge *bridge) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); 3488c2ecf20Sopenharmony_ci int ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (ps8622->enabled) 3518c2ecf20Sopenharmony_ci return; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci gpiod_set_value(ps8622->gpio_rst, 0); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (ps8622->v12) { 3568c2ecf20Sopenharmony_ci ret = regulator_enable(ps8622->v12); 3578c2ecf20Sopenharmony_ci if (ret) 3588c2ecf20Sopenharmony_ci DRM_ERROR("fails to enable ps8622->v12"); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci gpiod_set_value(ps8622->gpio_slp, 1); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * T1 is the range of time that it takes for the power to rise after we 3658c2ecf20Sopenharmony_ci * enable the lcd/ps8622 fet. T2 is the range of time in which the 3668c2ecf20Sopenharmony_ci * data sheet specifies we should deassert the reset pin. 3678c2ecf20Sopenharmony_ci * 3688c2ecf20Sopenharmony_ci * If it takes T1.max for the power to rise, we need to wait atleast 3698c2ecf20Sopenharmony_ci * T2.min before deasserting the reset pin. If it takes T1.min for the 3708c2ecf20Sopenharmony_ci * power to rise, we need to wait at most T2.max before deasserting the 3718c2ecf20Sopenharmony_ci * reset pin. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_ci usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US, 3748c2ecf20Sopenharmony_ci PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci gpiod_set_value(ps8622->gpio_rst, 1); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* wait 20ms after RST high */ 3798c2ecf20Sopenharmony_ci usleep_range(20000, 30000); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ret = ps8622_send_config(ps8622); 3828c2ecf20Sopenharmony_ci if (ret) { 3838c2ecf20Sopenharmony_ci DRM_ERROR("Failed to send config to bridge (%d)\n", ret); 3848c2ecf20Sopenharmony_ci return; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci ps8622->enabled = true; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void ps8622_disable(struct drm_bridge *bridge) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci /* Delay after panel is disabled */ 3938c2ecf20Sopenharmony_ci msleep(PS8622_PWMO_END_T12_MS); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void ps8622_post_disable(struct drm_bridge *bridge) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!ps8622->enabled) 4018c2ecf20Sopenharmony_ci return; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ps8622->enabled = false; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * This doesn't matter if the regulators are turned off, but something 4078c2ecf20Sopenharmony_ci * else might keep them on. In that case, we want to assert the slp gpio 4088c2ecf20Sopenharmony_ci * to lower power. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci gpiod_set_value(ps8622->gpio_slp, 0); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (ps8622->v12) 4138c2ecf20Sopenharmony_ci regulator_disable(ps8622->v12); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * Sleep for at least the amount of time that it takes the power rail to 4178c2ecf20Sopenharmony_ci * fall to prevent asserting the rst gpio from doing anything. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci usleep_range(PS8622_POWER_FALL_T16_MAX_US, 4208c2ecf20Sopenharmony_ci 2 * PS8622_POWER_FALL_T16_MAX_US); 4218c2ecf20Sopenharmony_ci gpiod_set_value(ps8622->gpio_rst, 0); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci msleep(PS8622_POWER_OFF_T17_MS); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int ps8622_attach(struct drm_bridge *bridge, 4278c2ecf20Sopenharmony_ci enum drm_bridge_attach_flags flags) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return drm_bridge_attach(ps8622->bridge.encoder, ps8622->panel_bridge, 4328c2ecf20Sopenharmony_ci &ps8622->bridge, flags); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs ps8622_bridge_funcs = { 4368c2ecf20Sopenharmony_ci .pre_enable = ps8622_pre_enable, 4378c2ecf20Sopenharmony_ci .disable = ps8622_disable, 4388c2ecf20Sopenharmony_ci .post_disable = ps8622_post_disable, 4398c2ecf20Sopenharmony_ci .attach = ps8622_attach, 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic const struct of_device_id ps8622_devices[] = { 4438c2ecf20Sopenharmony_ci {.compatible = "parade,ps8622",}, 4448c2ecf20Sopenharmony_ci {.compatible = "parade,ps8625",}, 4458c2ecf20Sopenharmony_ci {} 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ps8622_devices); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int ps8622_probe(struct i2c_client *client, 4508c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 4538c2ecf20Sopenharmony_ci struct ps8622_bridge *ps8622; 4548c2ecf20Sopenharmony_ci struct drm_bridge *panel_bridge; 4558c2ecf20Sopenharmony_ci struct drm_panel *panel; 4568c2ecf20Sopenharmony_ci int ret; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL); 4598c2ecf20Sopenharmony_ci if (!ps8622) 4608c2ecf20Sopenharmony_ci return -ENOMEM; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL); 4638c2ecf20Sopenharmony_ci if (ret) 4648c2ecf20Sopenharmony_ci return ret; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci panel_bridge = devm_drm_panel_bridge_add(dev, panel); 4678c2ecf20Sopenharmony_ci if (IS_ERR(panel_bridge)) 4688c2ecf20Sopenharmony_ci return PTR_ERR(panel_bridge); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ps8622->panel_bridge = panel_bridge; 4718c2ecf20Sopenharmony_ci ps8622->client = client; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ps8622->v12 = devm_regulator_get(dev, "vdd12"); 4748c2ecf20Sopenharmony_ci if (IS_ERR(ps8622->v12)) { 4758c2ecf20Sopenharmony_ci dev_info(dev, "no 1.2v regulator found for PS8622\n"); 4768c2ecf20Sopenharmony_ci ps8622->v12 = NULL; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ps8622->gpio_slp = devm_gpiod_get(dev, "sleep", GPIOD_OUT_HIGH); 4808c2ecf20Sopenharmony_ci if (IS_ERR(ps8622->gpio_slp)) { 4818c2ecf20Sopenharmony_ci ret = PTR_ERR(ps8622->gpio_slp); 4828c2ecf20Sopenharmony_ci dev_err(dev, "cannot get gpio_slp %d\n", ret); 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* 4878c2ecf20Sopenharmony_ci * Assert the reset pin high to avoid the bridge being 4888c2ecf20Sopenharmony_ci * initialized prematurely 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci ps8622->gpio_rst = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 4918c2ecf20Sopenharmony_ci if (IS_ERR(ps8622->gpio_rst)) { 4928c2ecf20Sopenharmony_ci ret = PTR_ERR(ps8622->gpio_rst); 4938c2ecf20Sopenharmony_ci dev_err(dev, "cannot get gpio_rst %d\n", ret); 4948c2ecf20Sopenharmony_ci return ret; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci ps8622->max_lane_count = id->driver_data; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "lane-count", 5008c2ecf20Sopenharmony_ci &ps8622->lane_count)) { 5018c2ecf20Sopenharmony_ci ps8622->lane_count = ps8622->max_lane_count; 5028c2ecf20Sopenharmony_ci } else if (ps8622->lane_count > ps8622->max_lane_count) { 5038c2ecf20Sopenharmony_ci dev_info(dev, "lane-count property is too high," 5048c2ecf20Sopenharmony_ci "using max_lane_count\n"); 5058c2ecf20Sopenharmony_ci ps8622->lane_count = ps8622->max_lane_count; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (!of_find_property(dev->of_node, "use-external-pwm", NULL)) { 5098c2ecf20Sopenharmony_ci ps8622->bl = backlight_device_register("ps8622-backlight", 5108c2ecf20Sopenharmony_ci dev, ps8622, &ps8622_backlight_ops, 5118c2ecf20Sopenharmony_ci NULL); 5128c2ecf20Sopenharmony_ci if (IS_ERR(ps8622->bl)) { 5138c2ecf20Sopenharmony_ci DRM_ERROR("failed to register backlight\n"); 5148c2ecf20Sopenharmony_ci ret = PTR_ERR(ps8622->bl); 5158c2ecf20Sopenharmony_ci ps8622->bl = NULL; 5168c2ecf20Sopenharmony_ci return ret; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci ps8622->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS; 5198c2ecf20Sopenharmony_ci ps8622->bl->props.brightness = PS8622_MAX_BRIGHTNESS; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci ps8622->bridge.funcs = &ps8622_bridge_funcs; 5238c2ecf20Sopenharmony_ci ps8622->bridge.type = DRM_MODE_CONNECTOR_LVDS; 5248c2ecf20Sopenharmony_ci ps8622->bridge.of_node = dev->of_node; 5258c2ecf20Sopenharmony_ci drm_bridge_add(&ps8622->bridge); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci i2c_set_clientdata(client, ps8622); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic int ps8622_remove(struct i2c_client *client) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct ps8622_bridge *ps8622 = i2c_get_clientdata(client); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci backlight_device_unregister(ps8622->bl); 5378c2ecf20Sopenharmony_ci drm_bridge_remove(&ps8622->bridge); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic const struct i2c_device_id ps8622_i2c_table[] = { 5438c2ecf20Sopenharmony_ci /* Device type, max_lane_count */ 5448c2ecf20Sopenharmony_ci {"ps8622", 1}, 5458c2ecf20Sopenharmony_ci {"ps8625", 2}, 5468c2ecf20Sopenharmony_ci {}, 5478c2ecf20Sopenharmony_ci}; 5488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ps8622_i2c_table); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic struct i2c_driver ps8622_driver = { 5518c2ecf20Sopenharmony_ci .id_table = ps8622_i2c_table, 5528c2ecf20Sopenharmony_ci .probe = ps8622_probe, 5538c2ecf20Sopenharmony_ci .remove = ps8622_remove, 5548c2ecf20Sopenharmony_ci .driver = { 5558c2ecf20Sopenharmony_ci .name = "ps8622", 5568c2ecf20Sopenharmony_ci .of_match_table = ps8622_devices, 5578c2ecf20Sopenharmony_ci }, 5588c2ecf20Sopenharmony_ci}; 5598c2ecf20Sopenharmony_cimodule_i2c_driver(ps8622_driver); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vincent Palatin <vpalatin@chromium.org>"); 5628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Parade ps8622/ps8625 eDP-LVDS converter driver"); 5638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 564