Path: blob/master/drivers/gpu/drm/bridge/parade-ps8622.c
26494 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Parade PS8622 eDP/LVDS bridge driver3*4* Copyright (C) 2014 Google, Inc.5*/67#include <linux/backlight.h>8#include <linux/delay.h>9#include <linux/err.h>10#include <linux/gpio/consumer.h>11#include <linux/i2c.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/pm.h>15#include <linux/regulator/consumer.h>1617#include <drm/drm_atomic_helper.h>18#include <drm/drm_bridge.h>19#include <drm/drm_crtc.h>20#include <drm/drm_of.h>21#include <drm/drm_print.h>22#include <drm/drm_probe_helper.h>2324/* Brightness scale on the Parade chip */25#define PS8622_MAX_BRIGHTNESS 0xff2627/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */28#define PS8622_POWER_RISE_T1_MIN_US 1029#define PS8622_POWER_RISE_T1_MAX_US 1000030#define PS8622_RST_HIGH_T2_MIN_US 300031#define PS8622_RST_HIGH_T2_MAX_US 3000032#define PS8622_PWMO_END_T12_MS 20033#define PS8622_POWER_FALL_T16_MAX_US 1000034#define PS8622_POWER_OFF_T17_MS 5003536#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \37(PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US))38#error "T2.min + T1.max must be less than T2.max + T1.min"39#endif4041struct ps8622_bridge {42struct i2c_client *client;43struct drm_bridge bridge;44struct drm_bridge *panel_bridge;45struct regulator *v12;46struct backlight_device *bl;4748struct gpio_desc *gpio_slp;49struct gpio_desc *gpio_rst;5051u32 max_lane_count;52u32 lane_count;5354bool enabled;55};5657static inline struct ps8622_bridge *58bridge_to_ps8622(struct drm_bridge *bridge)59{60return container_of(bridge, struct ps8622_bridge, bridge);61}6263static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val)64{65int ret;66struct i2c_adapter *adap = client->adapter;67struct i2c_msg msg;68u8 data[] = {reg, val};6970msg.addr = client->addr + page;71msg.flags = 0;72msg.len = sizeof(data);73msg.buf = data;7475ret = i2c_transfer(adap, &msg, 1);76if (ret != 1)77pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n",78client->addr + page, reg, val, ret);79return !(ret == 1);80}8182static int ps8622_send_config(struct ps8622_bridge *ps8622)83{84struct i2c_client *cl = ps8622->client;85int err = 0;8687/* HPD low */88err = ps8622_set(cl, 0x02, 0xa1, 0x01);89if (err)90goto error;9192/* SW setting: [1:0] SW output 1.2V voltage is lower to 96% */93err = ps8622_set(cl, 0x04, 0x14, 0x01);94if (err)95goto error;9697/* RCO SS setting: [5:4] = b01 0.5%, b10 1%, b11 1.5% */98err = ps8622_set(cl, 0x04, 0xe3, 0x20);99if (err)100goto error;101102/* [7] RCO SS enable */103err = ps8622_set(cl, 0x04, 0xe2, 0x80);104if (err)105goto error;106107/* RPHY Setting108* [3:2] CDR tune wait cycle before measure for fine tune109* b00: 1us b01: 0.5us b10:2us, b11: 4us110*/111err = ps8622_set(cl, 0x04, 0x8a, 0x0c);112if (err)113goto error;114115/* [3] RFD always on */116err = ps8622_set(cl, 0x04, 0x89, 0x08);117if (err)118goto error;119120/* CTN lock in/out: 20000ppm/80000ppm. Lock out 2 times. */121err = ps8622_set(cl, 0x04, 0x71, 0x2d);122if (err)123goto error;124125/* 2.7G CDR settings: NOF=40LSB for HBR CDR setting */126err = ps8622_set(cl, 0x04, 0x7d, 0x07);127if (err)128goto error;129130/* [1:0] Fmin=+4bands */131err = ps8622_set(cl, 0x04, 0x7b, 0x00);132if (err)133goto error;134135/* [7:5] DCO_FTRNG=+-40% */136err = ps8622_set(cl, 0x04, 0x7a, 0xfd);137if (err)138goto error;139140/* 1.62G CDR settings: [5:2]NOF=64LSB [1:0]DCO scale is 2/5 */141err = ps8622_set(cl, 0x04, 0xc0, 0x12);142if (err)143goto error;144145/* Gitune=-37% */146err = ps8622_set(cl, 0x04, 0xc1, 0x92);147if (err)148goto error;149150/* Fbstep=100% */151err = ps8622_set(cl, 0x04, 0xc2, 0x1c);152if (err)153goto error;154155/* [7] LOS signal disable */156err = ps8622_set(cl, 0x04, 0x32, 0x80);157if (err)158goto error;159160/* RPIO Setting: [7:4] LVDS driver bias current : 75% (250mV swing) */161err = ps8622_set(cl, 0x04, 0x00, 0xb0);162if (err)163goto error;164165/* [7:6] Right-bar GPIO output strength is 8mA */166err = ps8622_set(cl, 0x04, 0x15, 0x40);167if (err)168goto error;169170/* EQ Training State Machine Setting, RCO calibration start */171err = ps8622_set(cl, 0x04, 0x54, 0x10);172if (err)173goto error;174175/* Logic, needs more than 10 I2C command */176/* [4:0] MAX_LANE_COUNT set to max supported lanes */177err = ps8622_set(cl, 0x01, 0x02, 0x80 | ps8622->max_lane_count);178if (err)179goto error;180181/* [4:0] LANE_COUNT_SET set to chosen lane count */182err = ps8622_set(cl, 0x01, 0x21, 0x80 | ps8622->lane_count);183if (err)184goto error;185186err = ps8622_set(cl, 0x00, 0x52, 0x20);187if (err)188goto error;189190/* HPD CP toggle enable */191err = ps8622_set(cl, 0x00, 0xf1, 0x03);192if (err)193goto error;194195err = ps8622_set(cl, 0x00, 0x62, 0x41);196if (err)197goto error;198199/* Counter number, add 1ms counter delay */200err = ps8622_set(cl, 0x00, 0xf6, 0x01);201if (err)202goto error;203204/* [6]PWM function control by DPCD0040f[7], default is PWM block */205err = ps8622_set(cl, 0x00, 0x77, 0x06);206if (err)207goto error;208209/* 04h Adjust VTotal toleranceto fix the 30Hz no display issue */210err = ps8622_set(cl, 0x00, 0x4c, 0x04);211if (err)212goto error;213214/* DPCD00400='h00, Parade OUI ='h001cf8 */215err = ps8622_set(cl, 0x01, 0xc0, 0x00);216if (err)217goto error;218219/* DPCD00401='h1c */220err = ps8622_set(cl, 0x01, 0xc1, 0x1c);221if (err)222goto error;223224/* DPCD00402='hf8 */225err = ps8622_set(cl, 0x01, 0xc2, 0xf8);226if (err)227goto error;228229/* DPCD403~408 = ASCII code, D2SLV5='h4432534c5635 */230err = ps8622_set(cl, 0x01, 0xc3, 0x44);231if (err)232goto error;233234/* DPCD404 */235err = ps8622_set(cl, 0x01, 0xc4, 0x32);236if (err)237goto error;238239/* DPCD405 */240err = ps8622_set(cl, 0x01, 0xc5, 0x53);241if (err)242goto error;243244/* DPCD406 */245err = ps8622_set(cl, 0x01, 0xc6, 0x4c);246if (err)247goto error;248249/* DPCD407 */250err = ps8622_set(cl, 0x01, 0xc7, 0x56);251if (err)252goto error;253254/* DPCD408 */255err = ps8622_set(cl, 0x01, 0xc8, 0x35);256if (err)257goto error;258259/* DPCD40A, Initial Code major revision '01' */260err = ps8622_set(cl, 0x01, 0xca, 0x01);261if (err)262goto error;263264/* DPCD40B, Initial Code minor revision '05' */265err = ps8622_set(cl, 0x01, 0xcb, 0x05);266if (err)267goto error;268269270if (ps8622->bl) {271/* DPCD720, internal PWM */272err = ps8622_set(cl, 0x01, 0xa5, 0xa0);273if (err)274goto error;275276/* FFh for 100% brightness, 0h for 0% brightness */277err = ps8622_set(cl, 0x01, 0xa7,278ps8622->bl->props.brightness);279if (err)280goto error;281} else {282/* DPCD720, external PWM */283err = ps8622_set(cl, 0x01, 0xa5, 0x80);284if (err)285goto error;286}287288/* Set LVDS output as 6bit-VESA mapping, single LVDS channel */289err = ps8622_set(cl, 0x01, 0xcc, 0x13);290if (err)291goto error;292293/* Enable SSC set by register */294err = ps8622_set(cl, 0x02, 0xb1, 0x20);295if (err)296goto error;297298/* Set SSC enabled and +/-1% central spreading */299err = ps8622_set(cl, 0x04, 0x10, 0x16);300if (err)301goto error;302303/* Logic end */304/* MPU Clock source: LC => RCO */305err = ps8622_set(cl, 0x04, 0x59, 0x60);306if (err)307goto error;308309/* LC -> RCO */310err = ps8622_set(cl, 0x04, 0x54, 0x14);311if (err)312goto error;313314/* HPD high */315err = ps8622_set(cl, 0x02, 0xa1, 0x91);316317error:318return err ? -EIO : 0;319}320321static int ps8622_backlight_update(struct backlight_device *bl)322{323struct ps8622_bridge *ps8622 = dev_get_drvdata(&bl->dev);324int ret, brightness = backlight_get_brightness(bl);325326if (!ps8622->enabled)327return -EINVAL;328329ret = ps8622_set(ps8622->client, 0x01, 0xa7, brightness);330331return ret;332}333334static const struct backlight_ops ps8622_backlight_ops = {335.update_status = ps8622_backlight_update,336};337338static void ps8622_pre_enable(struct drm_bridge *bridge)339{340struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);341int ret;342343if (ps8622->enabled)344return;345346gpiod_set_value(ps8622->gpio_rst, 0);347348if (ps8622->v12) {349ret = regulator_enable(ps8622->v12);350if (ret)351DRM_ERROR("fails to enable ps8622->v12");352}353354gpiod_set_value(ps8622->gpio_slp, 1);355356/*357* T1 is the range of time that it takes for the power to rise after we358* enable the lcd/ps8622 fet. T2 is the range of time in which the359* data sheet specifies we should deassert the reset pin.360*361* If it takes T1.max for the power to rise, we need to wait atleast362* T2.min before deasserting the reset pin. If it takes T1.min for the363* power to rise, we need to wait at most T2.max before deasserting the364* reset pin.365*/366usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US,367PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US);368369gpiod_set_value(ps8622->gpio_rst, 1);370371/* wait 20ms after RST high */372usleep_range(20000, 30000);373374ret = ps8622_send_config(ps8622);375if (ret) {376DRM_ERROR("Failed to send config to bridge (%d)\n", ret);377return;378}379380ps8622->enabled = true;381}382383static void ps8622_disable(struct drm_bridge *bridge)384{385/* Delay after panel is disabled */386msleep(PS8622_PWMO_END_T12_MS);387}388389static void ps8622_post_disable(struct drm_bridge *bridge)390{391struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);392393if (!ps8622->enabled)394return;395396ps8622->enabled = false;397398/*399* This doesn't matter if the regulators are turned off, but something400* else might keep them on. In that case, we want to assert the slp gpio401* to lower power.402*/403gpiod_set_value(ps8622->gpio_slp, 0);404405if (ps8622->v12)406regulator_disable(ps8622->v12);407408/*409* Sleep for at least the amount of time that it takes the power rail to410* fall to prevent asserting the rst gpio from doing anything.411*/412usleep_range(PS8622_POWER_FALL_T16_MAX_US,4132 * PS8622_POWER_FALL_T16_MAX_US);414gpiod_set_value(ps8622->gpio_rst, 0);415416msleep(PS8622_POWER_OFF_T17_MS);417}418419static int ps8622_attach(struct drm_bridge *bridge,420struct drm_encoder *encoder,421enum drm_bridge_attach_flags flags)422{423struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);424425return drm_bridge_attach(ps8622->bridge.encoder, ps8622->panel_bridge,426&ps8622->bridge, flags);427}428429static const struct drm_bridge_funcs ps8622_bridge_funcs = {430.pre_enable = ps8622_pre_enable,431.disable = ps8622_disable,432.post_disable = ps8622_post_disable,433.attach = ps8622_attach,434};435436static const struct of_device_id ps8622_devices[] = {437{.compatible = "parade,ps8622",},438{.compatible = "parade,ps8625",},439{}440};441MODULE_DEVICE_TABLE(of, ps8622_devices);442443static int ps8622_probe(struct i2c_client *client)444{445const struct i2c_device_id *id = i2c_client_get_device_id(client);446struct device *dev = &client->dev;447struct ps8622_bridge *ps8622;448struct drm_bridge *panel_bridge;449int ret;450451ps8622 = devm_drm_bridge_alloc(dev, struct ps8622_bridge, bridge,452&ps8622_bridge_funcs);453if (IS_ERR(ps8622))454return PTR_ERR(ps8622);455456panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);457if (IS_ERR(panel_bridge))458return PTR_ERR(panel_bridge);459460ps8622->panel_bridge = panel_bridge;461ps8622->client = client;462463ps8622->v12 = devm_regulator_get(dev, "vdd12");464if (IS_ERR(ps8622->v12)) {465dev_info(dev, "no 1.2v regulator found for PS8622\n");466ps8622->v12 = NULL;467}468469ps8622->gpio_slp = devm_gpiod_get(dev, "sleep", GPIOD_OUT_HIGH);470if (IS_ERR(ps8622->gpio_slp)) {471ret = PTR_ERR(ps8622->gpio_slp);472dev_err(dev, "cannot get gpio_slp %d\n", ret);473return ret;474}475476/*477* Assert the reset pin high to avoid the bridge being478* initialized prematurely479*/480ps8622->gpio_rst = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);481if (IS_ERR(ps8622->gpio_rst)) {482ret = PTR_ERR(ps8622->gpio_rst);483dev_err(dev, "cannot get gpio_rst %d\n", ret);484return ret;485}486487ps8622->max_lane_count = id->driver_data;488489if (of_property_read_u32(dev->of_node, "lane-count",490&ps8622->lane_count)) {491ps8622->lane_count = ps8622->max_lane_count;492} else if (ps8622->lane_count > ps8622->max_lane_count) {493dev_info(dev, "lane-count property is too high,"494"using max_lane_count\n");495ps8622->lane_count = ps8622->max_lane_count;496}497498if (!of_property_read_bool(dev->of_node, "use-external-pwm")) {499ps8622->bl = backlight_device_register("ps8622-backlight",500dev, ps8622, &ps8622_backlight_ops,501NULL);502if (IS_ERR(ps8622->bl)) {503DRM_ERROR("failed to register backlight\n");504ret = PTR_ERR(ps8622->bl);505ps8622->bl = NULL;506return ret;507}508ps8622->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS;509ps8622->bl->props.brightness = PS8622_MAX_BRIGHTNESS;510}511512ps8622->bridge.type = DRM_MODE_CONNECTOR_LVDS;513ps8622->bridge.of_node = dev->of_node;514drm_bridge_add(&ps8622->bridge);515516i2c_set_clientdata(client, ps8622);517518return 0;519}520521static void ps8622_remove(struct i2c_client *client)522{523struct ps8622_bridge *ps8622 = i2c_get_clientdata(client);524525backlight_device_unregister(ps8622->bl);526drm_bridge_remove(&ps8622->bridge);527}528529static const struct i2c_device_id ps8622_i2c_table[] = {530/* Device type, max_lane_count */531{"ps8622", 1},532{"ps8625", 2},533{},534};535MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table);536537static struct i2c_driver ps8622_driver = {538.id_table = ps8622_i2c_table,539.probe = ps8622_probe,540.remove = ps8622_remove,541.driver = {542.name = "ps8622",543.of_match_table = ps8622_devices,544},545};546module_i2c_driver(ps8622_driver);547548MODULE_AUTHOR("Vincent Palatin <[email protected]>");549MODULE_DESCRIPTION("Parade ps8622/ps8625 eDP-LVDS converter driver");550MODULE_LICENSE("GPL v2");551552553