// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
* Copyright (c) 2019-2020. Linaro Limited.
*/
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <sound/hdmi-codec.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#define EDID_SEG_SIZE 256
#define EDID_LEN 32
#define EDID_LOOP 8
#define KEY_DDC_ACCS_DONE 0x02
#define DDC_NO_ACK 0x50
#define LT9611_4LANES 0
struct lt9611 {
struct device *dev;
struct drm_bridge bridge;
struct drm_connector connector;
struct regmap *regmap;
struct device_node *dsi0_node;
struct device_node *dsi1_node;
struct mipi_dsi_device *dsi0;
struct mipi_dsi_device *dsi1;
struct platform_device *audio_pdev;
bool ac_mode;
struct gpio_desc *reset_gpio;
struct gpio_desc *enable_gpio;
bool power_on;
bool sleep;
struct regulator_bulk_data supplies[2];
struct i2c_client *client;
enum drm_connector_status status;
u8 edid_buf[EDID_SEG_SIZE];
u32 vic;
};
#define LT9611_PAGE_CONTROL 0xff
static const struct regmap_range_cfg lt9611_ranges[] = {
{
.name = "register_range",
.range_min = 0,
.range_max = 0x85ff,
.selector_reg = LT9611_PAGE_CONTROL,
.selector_mask = 0xff,
.selector_shift = 0,
.window_start = 0,
.window_len = 0x100,
},
};
static const struct regmap_config lt9611_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xffff,
.ranges = lt9611_ranges,
.num_ranges = ARRAY_SIZE(lt9611_ranges),
};
struct lt9611_mode {
u16 hdisplay;
u16 vdisplay;
u8 vrefresh;
u8 lanes;
u8 intfs;
};
static struct lt9611_mode lt9611_modes[] = {
{ 3840, 2160, 30, 4, 2 }, /* 3840x2160 24bit 30Hz 4Lane 2ports */
{ 1920, 1080, 60, 4, 1 }, /* 1080P 24bit 60Hz 4lane 1port */
{ 1920, 1080, 30, 3, 1 }, /* 1080P 24bit 30Hz 3lane 1port */
{ 1920, 1080, 24, 3, 1 },
{ 720, 480, 60, 4, 1 },
{ 720, 576, 50, 2, 1 },
{ 640, 480, 60, 2, 1 },
};
static struct lt9611 *bridge_to_lt9611(struct drm_bridge *bridge)
{
return container_of(bridge, struct lt9611, bridge);
}
static struct lt9611 *connector_to_lt9611(struct drm_connector *connector)
{
return container_of(connector, struct lt9611, connector);
}
static int lt9611_mipi_input_analog(struct lt9611 *lt9611)
{
const struct reg_sequence reg_cfg[] = {
{ 0x8106, 0x40 }, /* port A rx current */
{ 0x810a, 0xfe }, /* port A ldo voltage set */
{ 0x810b, 0xbf }, /* enable port A lprx */
{ 0x8111, 0x40 }, /* port B rx current */
{ 0x8115, 0xfe }, /* port B ldo voltage set */
{ 0x8116, 0xbf }, /* enable port B lprx */
{ 0x811c, 0x03 }, /* PortA clk lane no-LP mode */
{ 0x8120, 0x03 }, /* PortB clk lane with-LP mode */
};
return regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg));
}
static int lt9611_mipi_input_digital(struct lt9611 *lt9611,
const struct drm_display_mode *mode