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