10cbbd5b1SDmitry Baryshkov // SPDX-License-Identifier: GPL-2.0
20cbbd5b1SDmitry Baryshkov /*
30cbbd5b1SDmitry Baryshkov * Copyright (c) 2018, The Linux Foundation. All rights reserved.
40cbbd5b1SDmitry Baryshkov * Copyright (c) 2019-2020. Linaro Limited.
50cbbd5b1SDmitry Baryshkov */
60cbbd5b1SDmitry Baryshkov
70cbbd5b1SDmitry Baryshkov #include <linux/firmware.h>
80cbbd5b1SDmitry Baryshkov #include <linux/gpio/consumer.h>
9a204f974SVille Syrjälä #include <linux/i2c.h>
100cbbd5b1SDmitry Baryshkov #include <linux/interrupt.h>
110cbbd5b1SDmitry Baryshkov #include <linux/module.h>
120cbbd5b1SDmitry Baryshkov #include <linux/mutex.h>
130cbbd5b1SDmitry Baryshkov #include <linux/of_graph.h>
140cbbd5b1SDmitry Baryshkov #include <linux/platform_device.h>
150cbbd5b1SDmitry Baryshkov #include <linux/regmap.h>
160cbbd5b1SDmitry Baryshkov #include <linux/regulator/consumer.h>
170cbbd5b1SDmitry Baryshkov #include <linux/wait.h>
18bc6fa867SDmitry Baryshkov #include <linux/workqueue.h>
190cbbd5b1SDmitry Baryshkov
200cbbd5b1SDmitry Baryshkov #include <sound/hdmi-codec.h>
210cbbd5b1SDmitry Baryshkov
220cbbd5b1SDmitry Baryshkov #include <drm/drm_atomic_helper.h>
230cbbd5b1SDmitry Baryshkov #include <drm/drm_bridge.h>
24a05f7279SJani Nikula #include <drm/drm_edid.h>
250cbbd5b1SDmitry Baryshkov #include <drm/drm_mipi_dsi.h>
260cbbd5b1SDmitry Baryshkov #include <drm/drm_print.h>
270cbbd5b1SDmitry Baryshkov #include <drm/drm_probe_helper.h>
280cbbd5b1SDmitry Baryshkov
290cbbd5b1SDmitry Baryshkov #define EDID_BLOCK_SIZE 128
300cbbd5b1SDmitry Baryshkov #define EDID_NUM_BLOCKS 2
310cbbd5b1SDmitry Baryshkov
32354c0fb6SJuerg Haefliger #define FW_FILE "lt9611uxc_fw.bin"
33354c0fb6SJuerg Haefliger
340cbbd5b1SDmitry Baryshkov struct lt9611uxc {
350cbbd5b1SDmitry Baryshkov struct device *dev;
360cbbd5b1SDmitry Baryshkov struct drm_bridge bridge;
370cbbd5b1SDmitry Baryshkov struct drm_connector connector;
380cbbd5b1SDmitry Baryshkov
390cbbd5b1SDmitry Baryshkov struct regmap *regmap;
400cbbd5b1SDmitry Baryshkov /* Protects all accesses to registers by stopping the on-chip MCU */
410cbbd5b1SDmitry Baryshkov struct mutex ocm_lock;
420cbbd5b1SDmitry Baryshkov
430cbbd5b1SDmitry Baryshkov struct wait_queue_head wq;
44bc6fa867SDmitry Baryshkov struct work_struct work;
450cbbd5b1SDmitry Baryshkov
460cbbd5b1SDmitry Baryshkov struct device_node *dsi0_node;
470cbbd5b1SDmitry Baryshkov struct device_node *dsi1_node;
480cbbd5b1SDmitry Baryshkov struct mipi_dsi_device *dsi0;
490cbbd5b1SDmitry Baryshkov struct mipi_dsi_device *dsi1;
500cbbd5b1SDmitry Baryshkov struct platform_device *audio_pdev;
510cbbd5b1SDmitry Baryshkov
520cbbd5b1SDmitry Baryshkov struct gpio_desc *reset_gpio;
530cbbd5b1SDmitry Baryshkov struct gpio_desc *enable_gpio;
540cbbd5b1SDmitry Baryshkov
550cbbd5b1SDmitry Baryshkov struct regulator_bulk_data supplies[2];
560cbbd5b1SDmitry Baryshkov
570cbbd5b1SDmitry Baryshkov struct i2c_client *client;
580cbbd5b1SDmitry Baryshkov
590cbbd5b1SDmitry Baryshkov bool hpd_supported;
600cbbd5b1SDmitry Baryshkov bool edid_read;
61bc6fa867SDmitry Baryshkov /* can be accessed from different threads, so protect this with ocm_lock */
62bc6fa867SDmitry Baryshkov bool hdmi_connected;
630cbbd5b1SDmitry Baryshkov uint8_t fw_version;
640cbbd5b1SDmitry Baryshkov };
650cbbd5b1SDmitry Baryshkov
660cbbd5b1SDmitry Baryshkov #define LT9611_PAGE_CONTROL 0xff
670cbbd5b1SDmitry Baryshkov
680cbbd5b1SDmitry Baryshkov static const struct regmap_range_cfg lt9611uxc_ranges[] = {
690cbbd5b1SDmitry Baryshkov {
700cbbd5b1SDmitry Baryshkov .name = "register_range",
710cbbd5b1SDmitry Baryshkov .range_min = 0,
720cbbd5b1SDmitry Baryshkov .range_max = 0xd0ff,
730cbbd5b1SDmitry Baryshkov .selector_reg = LT9611_PAGE_CONTROL,
740cbbd5b1SDmitry Baryshkov .selector_mask = 0xff,
750cbbd5b1SDmitry Baryshkov .selector_shift = 0,
760cbbd5b1SDmitry Baryshkov .window_start = 0,
770cbbd5b1SDmitry Baryshkov .window_len = 0x100,
780cbbd5b1SDmitry Baryshkov },
790cbbd5b1SDmitry Baryshkov };
800cbbd5b1SDmitry Baryshkov
810cbbd5b1SDmitry Baryshkov static const struct regmap_config lt9611uxc_regmap_config = {
820cbbd5b1SDmitry Baryshkov .reg_bits = 8,
830cbbd5b1SDmitry Baryshkov .val_bits = 8,
840cbbd5b1SDmitry Baryshkov .max_register = 0xffff,
850cbbd5b1SDmitry Baryshkov .ranges = lt9611uxc_ranges,
860cbbd5b1SDmitry Baryshkov .num_ranges = ARRAY_SIZE(lt9611uxc_ranges),
870cbbd5b1SDmitry Baryshkov };
880cbbd5b1SDmitry Baryshkov
890cbbd5b1SDmitry Baryshkov struct lt9611uxc_mode {
900cbbd5b1SDmitry Baryshkov u16 hdisplay;
910cbbd5b1SDmitry Baryshkov u16 vdisplay;
920cbbd5b1SDmitry Baryshkov u8 vrefresh;
930cbbd5b1SDmitry Baryshkov };
940cbbd5b1SDmitry Baryshkov
950cbbd5b1SDmitry Baryshkov /*
960cbbd5b1SDmitry Baryshkov * This chip supports only a fixed set of modes.
970cbbd5b1SDmitry Baryshkov * Enumerate them here to check whether the mode is supported.
980cbbd5b1SDmitry Baryshkov */
990cbbd5b1SDmitry Baryshkov static struct lt9611uxc_mode lt9611uxc_modes[] = {
1000cbbd5b1SDmitry Baryshkov { 1920, 1080, 60 },
1010cbbd5b1SDmitry Baryshkov { 1920, 1080, 30 },
1020cbbd5b1SDmitry Baryshkov { 1920, 1080, 25 },
1030cbbd5b1SDmitry Baryshkov { 1366, 768, 60 },
1040cbbd5b1SDmitry Baryshkov { 1360, 768, 60 },
1050cbbd5b1SDmitry Baryshkov { 1280, 1024, 60 },
1060cbbd5b1SDmitry Baryshkov { 1280, 800, 60 },
1070cbbd5b1SDmitry Baryshkov { 1280, 720, 60 },
1080cbbd5b1SDmitry Baryshkov { 1280, 720, 50 },
1090cbbd5b1SDmitry Baryshkov { 1280, 720, 30 },
1100cbbd5b1SDmitry Baryshkov { 1152, 864, 60 },
1110cbbd5b1SDmitry Baryshkov { 1024, 768, 60 },
1120cbbd5b1SDmitry Baryshkov { 800, 600, 60 },
1130cbbd5b1SDmitry Baryshkov { 720, 576, 50 },
1140cbbd5b1SDmitry Baryshkov { 720, 480, 60 },
1150cbbd5b1SDmitry Baryshkov { 640, 480, 60 },
1160cbbd5b1SDmitry Baryshkov };
1170cbbd5b1SDmitry Baryshkov
bridge_to_lt9611uxc(struct drm_bridge * bridge)1180cbbd5b1SDmitry Baryshkov static struct lt9611uxc *bridge_to_lt9611uxc(struct drm_bridge *bridge)
1190cbbd5b1SDmitry Baryshkov {
1200cbbd5b1SDmitry Baryshkov return container_of(bridge, struct lt9611uxc, bridge);
1210cbbd5b1SDmitry Baryshkov }
1220cbbd5b1SDmitry Baryshkov
connector_to_lt9611uxc(struct drm_connector * connector)1230cbbd5b1SDmitry Baryshkov static struct lt9611uxc *connector_to_lt9611uxc(struct drm_connector *connector)
1240cbbd5b1SDmitry Baryshkov {
1250cbbd5b1SDmitry Baryshkov return container_of(connector, struct lt9611uxc, connector);
1260cbbd5b1SDmitry Baryshkov }
1270cbbd5b1SDmitry Baryshkov
lt9611uxc_lock(struct lt9611uxc * lt9611uxc)1280cbbd5b1SDmitry Baryshkov static void lt9611uxc_lock(struct lt9611uxc *lt9611uxc)
1290cbbd5b1SDmitry Baryshkov {
1300cbbd5b1SDmitry Baryshkov mutex_lock(<9611uxc->ocm_lock);
1310cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0x80ee, 0x01);
1320cbbd5b1SDmitry Baryshkov }
1330cbbd5b1SDmitry Baryshkov
lt9611uxc_unlock(struct lt9611uxc * lt9611uxc)1340cbbd5b1SDmitry Baryshkov static void lt9611uxc_unlock(struct lt9611uxc *lt9611uxc)
1350cbbd5b1SDmitry Baryshkov {
1360cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0x80ee, 0x00);
1370cbbd5b1SDmitry Baryshkov msleep(50);
1380cbbd5b1SDmitry Baryshkov mutex_unlock(<9611uxc->ocm_lock);
1390cbbd5b1SDmitry Baryshkov }
1400cbbd5b1SDmitry Baryshkov
lt9611uxc_irq_thread_handler(int irq,void * dev_id)1410cbbd5b1SDmitry Baryshkov static irqreturn_t lt9611uxc_irq_thread_handler(int irq, void *dev_id)
1420cbbd5b1SDmitry Baryshkov {
1430cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = dev_id;
1440cbbd5b1SDmitry Baryshkov unsigned int irq_status = 0;
1450cbbd5b1SDmitry Baryshkov unsigned int hpd_status = 0;
1460cbbd5b1SDmitry Baryshkov
1470cbbd5b1SDmitry Baryshkov lt9611uxc_lock(lt9611uxc);
1480cbbd5b1SDmitry Baryshkov
1490cbbd5b1SDmitry Baryshkov regmap_read(lt9611uxc->regmap, 0xb022, &irq_status);
1500cbbd5b1SDmitry Baryshkov regmap_read(lt9611uxc->regmap, 0xb023, &hpd_status);
1510cbbd5b1SDmitry Baryshkov if (irq_status)
1520cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xb022, 0);
1530cbbd5b1SDmitry Baryshkov
154053b1b28SDmitry Baryshkov if (irq_status & BIT(0)) {
1550cbbd5b1SDmitry Baryshkov lt9611uxc->edid_read = !!(hpd_status & BIT(0));
156053b1b28SDmitry Baryshkov wake_up_all(<9611uxc->wq);
157053b1b28SDmitry Baryshkov }
1580cbbd5b1SDmitry Baryshkov
1590cbbd5b1SDmitry Baryshkov if (irq_status & BIT(1)) {
160bc6fa867SDmitry Baryshkov lt9611uxc->hdmi_connected = hpd_status & BIT(1);
161bc6fa867SDmitry Baryshkov schedule_work(<9611uxc->work);
1620cbbd5b1SDmitry Baryshkov }
1630cbbd5b1SDmitry Baryshkov
164bc6fa867SDmitry Baryshkov lt9611uxc_unlock(lt9611uxc);
165bc6fa867SDmitry Baryshkov
1660cbbd5b1SDmitry Baryshkov return IRQ_HANDLED;
1670cbbd5b1SDmitry Baryshkov }
1680cbbd5b1SDmitry Baryshkov
lt9611uxc_hpd_work(struct work_struct * work)169bc6fa867SDmitry Baryshkov static void lt9611uxc_hpd_work(struct work_struct *work)
170bc6fa867SDmitry Baryshkov {
171bc6fa867SDmitry Baryshkov struct lt9611uxc *lt9611uxc = container_of(work, struct lt9611uxc, work);
172bc6fa867SDmitry Baryshkov bool connected;
173bc6fa867SDmitry Baryshkov
17415184965SDmitry Baryshkov if (lt9611uxc->connector.dev) {
17515184965SDmitry Baryshkov if (lt9611uxc->connector.dev->mode_config.funcs)
176bc6fa867SDmitry Baryshkov drm_kms_helper_hotplug_event(lt9611uxc->connector.dev);
17715184965SDmitry Baryshkov } else {
178bc6fa867SDmitry Baryshkov
179bc6fa867SDmitry Baryshkov mutex_lock(<9611uxc->ocm_lock);
180bc6fa867SDmitry Baryshkov connected = lt9611uxc->hdmi_connected;
181bc6fa867SDmitry Baryshkov mutex_unlock(<9611uxc->ocm_lock);
182bc6fa867SDmitry Baryshkov
183bc6fa867SDmitry Baryshkov drm_bridge_hpd_notify(<9611uxc->bridge,
184bc6fa867SDmitry Baryshkov connected ?
185bc6fa867SDmitry Baryshkov connector_status_connected :
186bc6fa867SDmitry Baryshkov connector_status_disconnected);
187bc6fa867SDmitry Baryshkov }
188bc6fa867SDmitry Baryshkov }
189bc6fa867SDmitry Baryshkov
lt9611uxc_reset(struct lt9611uxc * lt9611uxc)1900cbbd5b1SDmitry Baryshkov static void lt9611uxc_reset(struct lt9611uxc *lt9611uxc)
1910cbbd5b1SDmitry Baryshkov {
1920cbbd5b1SDmitry Baryshkov gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 1);
1930cbbd5b1SDmitry Baryshkov msleep(20);
1940cbbd5b1SDmitry Baryshkov
1950cbbd5b1SDmitry Baryshkov gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 0);
1960cbbd5b1SDmitry Baryshkov msleep(20);
1970cbbd5b1SDmitry Baryshkov
1980cbbd5b1SDmitry Baryshkov gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 1);
1990cbbd5b1SDmitry Baryshkov msleep(300);
2000cbbd5b1SDmitry Baryshkov }
2010cbbd5b1SDmitry Baryshkov
lt9611uxc_assert_5v(struct lt9611uxc * lt9611uxc)2020cbbd5b1SDmitry Baryshkov static void lt9611uxc_assert_5v(struct lt9611uxc *lt9611uxc)
2030cbbd5b1SDmitry Baryshkov {
2040cbbd5b1SDmitry Baryshkov if (!lt9611uxc->enable_gpio)
2050cbbd5b1SDmitry Baryshkov return;
2060cbbd5b1SDmitry Baryshkov
2070cbbd5b1SDmitry Baryshkov gpiod_set_value_cansleep(lt9611uxc->enable_gpio, 1);
2080cbbd5b1SDmitry Baryshkov msleep(20);
2090cbbd5b1SDmitry Baryshkov }
2100cbbd5b1SDmitry Baryshkov
lt9611uxc_regulator_init(struct lt9611uxc * lt9611uxc)2110cbbd5b1SDmitry Baryshkov static int lt9611uxc_regulator_init(struct lt9611uxc *lt9611uxc)
2120cbbd5b1SDmitry Baryshkov {
2130cbbd5b1SDmitry Baryshkov int ret;
2140cbbd5b1SDmitry Baryshkov
2150cbbd5b1SDmitry Baryshkov lt9611uxc->supplies[0].supply = "vdd";
2160cbbd5b1SDmitry Baryshkov lt9611uxc->supplies[1].supply = "vcc";
2170cbbd5b1SDmitry Baryshkov
2180cbbd5b1SDmitry Baryshkov ret = devm_regulator_bulk_get(lt9611uxc->dev, 2, lt9611uxc->supplies);
2190cbbd5b1SDmitry Baryshkov if (ret < 0)
2200cbbd5b1SDmitry Baryshkov return ret;
2210cbbd5b1SDmitry Baryshkov
2220cbbd5b1SDmitry Baryshkov return regulator_set_load(lt9611uxc->supplies[0].consumer, 200000);
2230cbbd5b1SDmitry Baryshkov }
2240cbbd5b1SDmitry Baryshkov
lt9611uxc_regulator_enable(struct lt9611uxc * lt9611uxc)2250cbbd5b1SDmitry Baryshkov static int lt9611uxc_regulator_enable(struct lt9611uxc *lt9611uxc)
2260cbbd5b1SDmitry Baryshkov {
2270cbbd5b1SDmitry Baryshkov int ret;
2280cbbd5b1SDmitry Baryshkov
2290cbbd5b1SDmitry Baryshkov ret = regulator_enable(lt9611uxc->supplies[0].consumer);
2300cbbd5b1SDmitry Baryshkov if (ret < 0)
2310cbbd5b1SDmitry Baryshkov return ret;
2320cbbd5b1SDmitry Baryshkov
2330cbbd5b1SDmitry Baryshkov usleep_range(1000, 10000); /* 50000 according to dtsi */
2340cbbd5b1SDmitry Baryshkov
2350cbbd5b1SDmitry Baryshkov ret = regulator_enable(lt9611uxc->supplies[1].consumer);
2360cbbd5b1SDmitry Baryshkov if (ret < 0) {
2370cbbd5b1SDmitry Baryshkov regulator_disable(lt9611uxc->supplies[0].consumer);
2380cbbd5b1SDmitry Baryshkov return ret;
2390cbbd5b1SDmitry Baryshkov }
2400cbbd5b1SDmitry Baryshkov
2410cbbd5b1SDmitry Baryshkov return 0;
2420cbbd5b1SDmitry Baryshkov }
2430cbbd5b1SDmitry Baryshkov
lt9611uxc_find_mode(const struct drm_display_mode * mode)2440cbbd5b1SDmitry Baryshkov static struct lt9611uxc_mode *lt9611uxc_find_mode(const struct drm_display_mode *mode)
2450cbbd5b1SDmitry Baryshkov {
2460cbbd5b1SDmitry Baryshkov int i;
2470cbbd5b1SDmitry Baryshkov
2480cbbd5b1SDmitry Baryshkov for (i = 0; i < ARRAY_SIZE(lt9611uxc_modes); i++) {
2490cbbd5b1SDmitry Baryshkov if (lt9611uxc_modes[i].hdisplay == mode->hdisplay &&
2500cbbd5b1SDmitry Baryshkov lt9611uxc_modes[i].vdisplay == mode->vdisplay &&
2510cbbd5b1SDmitry Baryshkov lt9611uxc_modes[i].vrefresh == drm_mode_vrefresh(mode)) {
2520cbbd5b1SDmitry Baryshkov return <9611uxc_modes[i];
2530cbbd5b1SDmitry Baryshkov }
2540cbbd5b1SDmitry Baryshkov }
2550cbbd5b1SDmitry Baryshkov
2560cbbd5b1SDmitry Baryshkov return NULL;
2570cbbd5b1SDmitry Baryshkov }
2580cbbd5b1SDmitry Baryshkov
lt9611uxc_attach_dsi(struct lt9611uxc * lt9611uxc,struct device_node * dsi_node)2590cbbd5b1SDmitry Baryshkov static struct mipi_dsi_device *lt9611uxc_attach_dsi(struct lt9611uxc *lt9611uxc,
2600cbbd5b1SDmitry Baryshkov struct device_node *dsi_node)
2610cbbd5b1SDmitry Baryshkov {
2620cbbd5b1SDmitry Baryshkov const struct mipi_dsi_device_info info = { "lt9611uxc", 0, NULL };
2630cbbd5b1SDmitry Baryshkov struct mipi_dsi_device *dsi;
2640cbbd5b1SDmitry Baryshkov struct mipi_dsi_host *host;
265293ada7bSMaxime Ripard struct device *dev = lt9611uxc->dev;
2660cbbd5b1SDmitry Baryshkov int ret;
2670cbbd5b1SDmitry Baryshkov
2680cbbd5b1SDmitry Baryshkov host = of_find_mipi_dsi_host_by_node(dsi_node);
269*6d9e877cSNícolas F. R. A. Prado if (!host)
270*6d9e877cSNícolas F. R. A. Prado return ERR_PTR(dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n"));
2710cbbd5b1SDmitry Baryshkov
272293ada7bSMaxime Ripard dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
2730cbbd5b1SDmitry Baryshkov if (IS_ERR(dsi)) {
274293ada7bSMaxime Ripard dev_err(dev, "failed to create dsi device\n");
2750cbbd5b1SDmitry Baryshkov return dsi;
2760cbbd5b1SDmitry Baryshkov }
2770cbbd5b1SDmitry Baryshkov
2780cbbd5b1SDmitry Baryshkov dsi->lanes = 4;
2790cbbd5b1SDmitry Baryshkov dsi->format = MIPI_DSI_FMT_RGB888;
2800cbbd5b1SDmitry Baryshkov dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
2810cbbd5b1SDmitry Baryshkov MIPI_DSI_MODE_VIDEO_HSE;
2820cbbd5b1SDmitry Baryshkov
283293ada7bSMaxime Ripard ret = devm_mipi_dsi_attach(dev, dsi);
2840cbbd5b1SDmitry Baryshkov if (ret < 0) {
285293ada7bSMaxime Ripard dev_err(dev, "failed to attach dsi to host\n");
2860cbbd5b1SDmitry Baryshkov return ERR_PTR(ret);
2870cbbd5b1SDmitry Baryshkov }
2880cbbd5b1SDmitry Baryshkov
2890cbbd5b1SDmitry Baryshkov return dsi;
2900cbbd5b1SDmitry Baryshkov }
2910cbbd5b1SDmitry Baryshkov
lt9611uxc_connector_get_modes(struct drm_connector * connector)2920cbbd5b1SDmitry Baryshkov static int lt9611uxc_connector_get_modes(struct drm_connector *connector)
2930cbbd5b1SDmitry Baryshkov {
2940cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = connector_to_lt9611uxc(connector);
295392b6e9aSJani Nikula const struct drm_edid *drm_edid;
296b43a72c4SJani Nikula int count;
2970cbbd5b1SDmitry Baryshkov
298392b6e9aSJani Nikula drm_edid = drm_bridge_edid_read(<9611uxc->bridge, connector);
299392b6e9aSJani Nikula drm_edid_connector_update(connector, drm_edid);
300392b6e9aSJani Nikula count = drm_edid_connector_add_modes(connector);
301392b6e9aSJani Nikula drm_edid_free(drm_edid);
3020cbbd5b1SDmitry Baryshkov
3030cbbd5b1SDmitry Baryshkov return count;
3040cbbd5b1SDmitry Baryshkov }
3050cbbd5b1SDmitry Baryshkov
lt9611uxc_connector_detect(struct drm_connector * connector,bool force)3060cbbd5b1SDmitry Baryshkov static enum drm_connector_status lt9611uxc_connector_detect(struct drm_connector *connector,
3070cbbd5b1SDmitry Baryshkov bool force)
3080cbbd5b1SDmitry Baryshkov {
3090cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = connector_to_lt9611uxc(connector);
3100cbbd5b1SDmitry Baryshkov
3110cbbd5b1SDmitry Baryshkov return lt9611uxc->bridge.funcs->detect(<9611uxc->bridge);
3120cbbd5b1SDmitry Baryshkov }
3130cbbd5b1SDmitry Baryshkov
lt9611uxc_connector_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)3140cbbd5b1SDmitry Baryshkov static enum drm_mode_status lt9611uxc_connector_mode_valid(struct drm_connector *connector,
3150cbbd5b1SDmitry Baryshkov struct drm_display_mode *mode)
3160cbbd5b1SDmitry Baryshkov {
3170cbbd5b1SDmitry Baryshkov struct lt9611uxc_mode *lt9611uxc_mode = lt9611uxc_find_mode(mode);
3180cbbd5b1SDmitry Baryshkov
3190cbbd5b1SDmitry Baryshkov return lt9611uxc_mode ? MODE_OK : MODE_BAD;
3200cbbd5b1SDmitry Baryshkov }
3210cbbd5b1SDmitry Baryshkov
3220cbbd5b1SDmitry Baryshkov static const struct drm_connector_helper_funcs lt9611uxc_bridge_connector_helper_funcs = {
3230cbbd5b1SDmitry Baryshkov .get_modes = lt9611uxc_connector_get_modes,
3240cbbd5b1SDmitry Baryshkov .mode_valid = lt9611uxc_connector_mode_valid,
3250cbbd5b1SDmitry Baryshkov };
3260cbbd5b1SDmitry Baryshkov
3270cbbd5b1SDmitry Baryshkov static const struct drm_connector_funcs lt9611uxc_bridge_connector_funcs = {
3280cbbd5b1SDmitry Baryshkov .fill_modes = drm_helper_probe_single_connector_modes,
3290cbbd5b1SDmitry Baryshkov .detect = lt9611uxc_connector_detect,
3300cbbd5b1SDmitry Baryshkov .destroy = drm_connector_cleanup,
3310cbbd5b1SDmitry Baryshkov .reset = drm_atomic_helper_connector_reset,
3320cbbd5b1SDmitry Baryshkov .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
3330cbbd5b1SDmitry Baryshkov .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
3340cbbd5b1SDmitry Baryshkov };
3350cbbd5b1SDmitry Baryshkov
lt9611uxc_connector_init(struct drm_bridge * bridge,struct lt9611uxc * lt9611uxc)3360cbbd5b1SDmitry Baryshkov static int lt9611uxc_connector_init(struct drm_bridge *bridge, struct lt9611uxc *lt9611uxc)
3370cbbd5b1SDmitry Baryshkov {
3380cbbd5b1SDmitry Baryshkov int ret;
3390cbbd5b1SDmitry Baryshkov
3400cbbd5b1SDmitry Baryshkov if (!bridge->encoder) {
3410cbbd5b1SDmitry Baryshkov DRM_ERROR("Parent encoder object not found");
3420cbbd5b1SDmitry Baryshkov return -ENODEV;
3430cbbd5b1SDmitry Baryshkov }
3440cbbd5b1SDmitry Baryshkov
34515184965SDmitry Baryshkov lt9611uxc->connector.polled = DRM_CONNECTOR_POLL_HPD;
34615184965SDmitry Baryshkov
3470cbbd5b1SDmitry Baryshkov drm_connector_helper_add(<9611uxc->connector,
3480cbbd5b1SDmitry Baryshkov <9611uxc_bridge_connector_helper_funcs);
3490cbbd5b1SDmitry Baryshkov ret = drm_connector_init(bridge->dev, <9611uxc->connector,
3500cbbd5b1SDmitry Baryshkov <9611uxc_bridge_connector_funcs,
3510cbbd5b1SDmitry Baryshkov DRM_MODE_CONNECTOR_HDMIA);
3520cbbd5b1SDmitry Baryshkov if (ret) {
3530cbbd5b1SDmitry Baryshkov DRM_ERROR("Failed to initialize connector with drm\n");
3540cbbd5b1SDmitry Baryshkov return ret;
3550cbbd5b1SDmitry Baryshkov }
3560cbbd5b1SDmitry Baryshkov
3570cbbd5b1SDmitry Baryshkov return drm_connector_attach_encoder(<9611uxc->connector, bridge->encoder);
3580cbbd5b1SDmitry Baryshkov }
3590cbbd5b1SDmitry Baryshkov
lt9611uxc_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)3600cbbd5b1SDmitry Baryshkov static int lt9611uxc_bridge_attach(struct drm_bridge *bridge,
3610cbbd5b1SDmitry Baryshkov enum drm_bridge_attach_flags flags)
3620cbbd5b1SDmitry Baryshkov {
3630cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
3640cbbd5b1SDmitry Baryshkov int ret;
3650cbbd5b1SDmitry Baryshkov
3660cbbd5b1SDmitry Baryshkov if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
3670cbbd5b1SDmitry Baryshkov ret = lt9611uxc_connector_init(bridge, lt9611uxc);
3680cbbd5b1SDmitry Baryshkov if (ret < 0)
3690cbbd5b1SDmitry Baryshkov return ret;
3700cbbd5b1SDmitry Baryshkov }
3710cbbd5b1SDmitry Baryshkov
3720cbbd5b1SDmitry Baryshkov return 0;
3730cbbd5b1SDmitry Baryshkov }
3740cbbd5b1SDmitry Baryshkov
3750cbbd5b1SDmitry Baryshkov static enum drm_mode_status
lt9611uxc_bridge_mode_valid(struct drm_bridge * bridge,const struct drm_display_info * info,const struct drm_display_mode * mode)3760cbbd5b1SDmitry Baryshkov lt9611uxc_bridge_mode_valid(struct drm_bridge *bridge,
3770cbbd5b1SDmitry Baryshkov const struct drm_display_info *info,
3780cbbd5b1SDmitry Baryshkov const struct drm_display_mode *mode)
3790cbbd5b1SDmitry Baryshkov {
3800cbbd5b1SDmitry Baryshkov struct lt9611uxc_mode *lt9611uxc_mode;
3810cbbd5b1SDmitry Baryshkov
3820cbbd5b1SDmitry Baryshkov lt9611uxc_mode = lt9611uxc_find_mode(mode);
3830cbbd5b1SDmitry Baryshkov
3840cbbd5b1SDmitry Baryshkov return lt9611uxc_mode ? MODE_OK : MODE_BAD;
3850cbbd5b1SDmitry Baryshkov }
3860cbbd5b1SDmitry Baryshkov
lt9611uxc_video_setup(struct lt9611uxc * lt9611uxc,const struct drm_display_mode * mode)3870cbbd5b1SDmitry Baryshkov static void lt9611uxc_video_setup(struct lt9611uxc *lt9611uxc,
3880cbbd5b1SDmitry Baryshkov const struct drm_display_mode *mode)
3890cbbd5b1SDmitry Baryshkov {
3900cbbd5b1SDmitry Baryshkov u32 h_total, hactive, hsync_len, hfront_porch;
3910cbbd5b1SDmitry Baryshkov u32 v_total, vactive, vsync_len, vfront_porch;
3920cbbd5b1SDmitry Baryshkov
3930cbbd5b1SDmitry Baryshkov h_total = mode->htotal;
3940cbbd5b1SDmitry Baryshkov v_total = mode->vtotal;
3950cbbd5b1SDmitry Baryshkov
3960cbbd5b1SDmitry Baryshkov hactive = mode->hdisplay;
3970cbbd5b1SDmitry Baryshkov hsync_len = mode->hsync_end - mode->hsync_start;
3980cbbd5b1SDmitry Baryshkov hfront_porch = mode->hsync_start - mode->hdisplay;
3990cbbd5b1SDmitry Baryshkov
4000cbbd5b1SDmitry Baryshkov vactive = mode->vdisplay;
4010cbbd5b1SDmitry Baryshkov vsync_len = mode->vsync_end - mode->vsync_start;
4020cbbd5b1SDmitry Baryshkov vfront_porch = mode->vsync_start - mode->vdisplay;
4030cbbd5b1SDmitry Baryshkov
4040cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd00d, (u8)(v_total / 256));
4050cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd00e, (u8)(v_total % 256));
4060cbbd5b1SDmitry Baryshkov
4070cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd00f, (u8)(vactive / 256));
4080cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd010, (u8)(vactive % 256));
4090cbbd5b1SDmitry Baryshkov
4100cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd011, (u8)(h_total / 256));
4110cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd012, (u8)(h_total % 256));
4120cbbd5b1SDmitry Baryshkov
4130cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd013, (u8)(hactive / 256));
4140cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd014, (u8)(hactive % 256));
4150cbbd5b1SDmitry Baryshkov
4160cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd015, (u8)(vsync_len % 256));
4170cbbd5b1SDmitry Baryshkov
4180cbbd5b1SDmitry Baryshkov regmap_update_bits(lt9611uxc->regmap, 0xd016, 0xf, (u8)(hsync_len / 256));
4190cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd017, (u8)(hsync_len % 256));
4200cbbd5b1SDmitry Baryshkov
4210cbbd5b1SDmitry Baryshkov regmap_update_bits(lt9611uxc->regmap, 0xd018, 0xf, (u8)(vfront_porch / 256));
4220cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd019, (u8)(vfront_porch % 256));
4230cbbd5b1SDmitry Baryshkov
4240cbbd5b1SDmitry Baryshkov regmap_update_bits(lt9611uxc->regmap, 0xd01a, 0xf, (u8)(hfront_porch / 256));
4250cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xd01b, (u8)(hfront_porch % 256));
4260cbbd5b1SDmitry Baryshkov }
4270cbbd5b1SDmitry Baryshkov
lt9611uxc_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adj_mode)4280cbbd5b1SDmitry Baryshkov static void lt9611uxc_bridge_mode_set(struct drm_bridge *bridge,
4290cbbd5b1SDmitry Baryshkov const struct drm_display_mode *mode,
4300cbbd5b1SDmitry Baryshkov const struct drm_display_mode *adj_mode)
4310cbbd5b1SDmitry Baryshkov {
4320cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
4330cbbd5b1SDmitry Baryshkov
4340cbbd5b1SDmitry Baryshkov lt9611uxc_lock(lt9611uxc);
4350cbbd5b1SDmitry Baryshkov lt9611uxc_video_setup(lt9611uxc, mode);
4360cbbd5b1SDmitry Baryshkov lt9611uxc_unlock(lt9611uxc);
4370cbbd5b1SDmitry Baryshkov }
4380cbbd5b1SDmitry Baryshkov
lt9611uxc_bridge_detect(struct drm_bridge * bridge)4390cbbd5b1SDmitry Baryshkov static enum drm_connector_status lt9611uxc_bridge_detect(struct drm_bridge *bridge)
4400cbbd5b1SDmitry Baryshkov {
4410cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
4420cbbd5b1SDmitry Baryshkov unsigned int reg_val = 0;
4430cbbd5b1SDmitry Baryshkov int ret;
444bc6fa867SDmitry Baryshkov bool connected = true;
445bc6fa867SDmitry Baryshkov
446bc6fa867SDmitry Baryshkov lt9611uxc_lock(lt9611uxc);
4470cbbd5b1SDmitry Baryshkov
4480cbbd5b1SDmitry Baryshkov if (lt9611uxc->hpd_supported) {
4490cbbd5b1SDmitry Baryshkov ret = regmap_read(lt9611uxc->regmap, 0xb023, ®_val);
4500cbbd5b1SDmitry Baryshkov
4510cbbd5b1SDmitry Baryshkov if (ret)
4520cbbd5b1SDmitry Baryshkov dev_err(lt9611uxc->dev, "failed to read hpd status: %d\n", ret);
4530cbbd5b1SDmitry Baryshkov else
4540cbbd5b1SDmitry Baryshkov connected = reg_val & BIT(1);
4550cbbd5b1SDmitry Baryshkov }
456bc6fa867SDmitry Baryshkov lt9611uxc->hdmi_connected = connected;
457bc6fa867SDmitry Baryshkov
458bc6fa867SDmitry Baryshkov lt9611uxc_unlock(lt9611uxc);
4590cbbd5b1SDmitry Baryshkov
4600cbbd5b1SDmitry Baryshkov return connected ? connector_status_connected :
4610cbbd5b1SDmitry Baryshkov connector_status_disconnected;
4620cbbd5b1SDmitry Baryshkov }
4630cbbd5b1SDmitry Baryshkov
lt9611uxc_wait_for_edid(struct lt9611uxc * lt9611uxc)4640cbbd5b1SDmitry Baryshkov static int lt9611uxc_wait_for_edid(struct lt9611uxc *lt9611uxc)
4650cbbd5b1SDmitry Baryshkov {
4660cbbd5b1SDmitry Baryshkov return wait_event_interruptible_timeout(lt9611uxc->wq, lt9611uxc->edid_read,
467053b1b28SDmitry Baryshkov msecs_to_jiffies(500));
4680cbbd5b1SDmitry Baryshkov }
4690cbbd5b1SDmitry Baryshkov
lt9611uxc_get_edid_block(void * data,u8 * buf,unsigned int block,size_t len)4700cbbd5b1SDmitry Baryshkov static int lt9611uxc_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
4710cbbd5b1SDmitry Baryshkov {
4720cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = data;
4730cbbd5b1SDmitry Baryshkov int ret;
4740cbbd5b1SDmitry Baryshkov
4750cbbd5b1SDmitry Baryshkov if (len > EDID_BLOCK_SIZE)
4760cbbd5b1SDmitry Baryshkov return -EINVAL;
4770cbbd5b1SDmitry Baryshkov
4780cbbd5b1SDmitry Baryshkov if (block >= EDID_NUM_BLOCKS)
4790cbbd5b1SDmitry Baryshkov return -EINVAL;
4800cbbd5b1SDmitry Baryshkov
4810cbbd5b1SDmitry Baryshkov lt9611uxc_lock(lt9611uxc);
4820cbbd5b1SDmitry Baryshkov
4830cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xb00b, 0x10);
4840cbbd5b1SDmitry Baryshkov
4850cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0xb00a, block * EDID_BLOCK_SIZE);
4860cbbd5b1SDmitry Baryshkov
4870cbbd5b1SDmitry Baryshkov ret = regmap_noinc_read(lt9611uxc->regmap, 0xb0b0, buf, len);
4880cbbd5b1SDmitry Baryshkov if (ret)
4890cbbd5b1SDmitry Baryshkov dev_err(lt9611uxc->dev, "edid read failed: %d\n", ret);
4900cbbd5b1SDmitry Baryshkov
4910cbbd5b1SDmitry Baryshkov lt9611uxc_unlock(lt9611uxc);
4920cbbd5b1SDmitry Baryshkov
4930cbbd5b1SDmitry Baryshkov return 0;
4940cbbd5b1SDmitry Baryshkov };
4950cbbd5b1SDmitry Baryshkov
lt9611uxc_bridge_edid_read(struct drm_bridge * bridge,struct drm_connector * connector)49626b2ddd8SJani Nikula static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *bridge,
4970cbbd5b1SDmitry Baryshkov struct drm_connector *connector)
4980cbbd5b1SDmitry Baryshkov {
4990cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
5000cbbd5b1SDmitry Baryshkov int ret;
5010cbbd5b1SDmitry Baryshkov
5020cbbd5b1SDmitry Baryshkov ret = lt9611uxc_wait_for_edid(lt9611uxc);
5030cbbd5b1SDmitry Baryshkov if (ret < 0) {
5040cbbd5b1SDmitry Baryshkov dev_err(lt9611uxc->dev, "wait for EDID failed: %d\n", ret);
5051bb7ab40SDmitry Baryshkov return NULL;
5061bb7ab40SDmitry Baryshkov } else if (ret == 0) {
5071bb7ab40SDmitry Baryshkov dev_err(lt9611uxc->dev, "wait for EDID timeout\n");
5081bb7ab40SDmitry Baryshkov return NULL;
5090cbbd5b1SDmitry Baryshkov }
5100cbbd5b1SDmitry Baryshkov
51126b2ddd8SJani Nikula return drm_edid_read_custom(connector, lt9611uxc_get_edid_block, lt9611uxc);
5120cbbd5b1SDmitry Baryshkov }
5130cbbd5b1SDmitry Baryshkov
5140cbbd5b1SDmitry Baryshkov static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = {
5150cbbd5b1SDmitry Baryshkov .attach = lt9611uxc_bridge_attach,
5160cbbd5b1SDmitry Baryshkov .mode_valid = lt9611uxc_bridge_mode_valid,
5170cbbd5b1SDmitry Baryshkov .mode_set = lt9611uxc_bridge_mode_set,
5180cbbd5b1SDmitry Baryshkov .detect = lt9611uxc_bridge_detect,
51926b2ddd8SJani Nikula .edid_read = lt9611uxc_bridge_edid_read,
5200cbbd5b1SDmitry Baryshkov };
5210cbbd5b1SDmitry Baryshkov
lt9611uxc_parse_dt(struct device * dev,struct lt9611uxc * lt9611uxc)5220cbbd5b1SDmitry Baryshkov static int lt9611uxc_parse_dt(struct device *dev,
5230cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc)
5240cbbd5b1SDmitry Baryshkov {
5250cbbd5b1SDmitry Baryshkov lt9611uxc->dsi0_node = of_graph_get_remote_node(dev->of_node, 0, -1);
5260cbbd5b1SDmitry Baryshkov if (!lt9611uxc->dsi0_node) {
5270cbbd5b1SDmitry Baryshkov dev_err(lt9611uxc->dev, "failed to get remote node for primary dsi\n");
5280cbbd5b1SDmitry Baryshkov return -ENODEV;
5290cbbd5b1SDmitry Baryshkov }
5300cbbd5b1SDmitry Baryshkov
5310cbbd5b1SDmitry Baryshkov lt9611uxc->dsi1_node = of_graph_get_remote_node(dev->of_node, 1, -1);
5320cbbd5b1SDmitry Baryshkov
5330cbbd5b1SDmitry Baryshkov return 0;
5340cbbd5b1SDmitry Baryshkov }
5350cbbd5b1SDmitry Baryshkov
lt9611uxc_gpio_init(struct lt9611uxc * lt9611uxc)5360cbbd5b1SDmitry Baryshkov static int lt9611uxc_gpio_init(struct lt9611uxc *lt9611uxc)
5370cbbd5b1SDmitry Baryshkov {
5380cbbd5b1SDmitry Baryshkov struct device *dev = lt9611uxc->dev;
5390cbbd5b1SDmitry Baryshkov
5400cbbd5b1SDmitry Baryshkov lt9611uxc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
5410cbbd5b1SDmitry Baryshkov if (IS_ERR(lt9611uxc->reset_gpio)) {
5420cbbd5b1SDmitry Baryshkov dev_err(dev, "failed to acquire reset gpio\n");
5430cbbd5b1SDmitry Baryshkov return PTR_ERR(lt9611uxc->reset_gpio);
5440cbbd5b1SDmitry Baryshkov }
5450cbbd5b1SDmitry Baryshkov
5460cbbd5b1SDmitry Baryshkov lt9611uxc->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
5470cbbd5b1SDmitry Baryshkov if (IS_ERR(lt9611uxc->enable_gpio)) {
5480cbbd5b1SDmitry Baryshkov dev_err(dev, "failed to acquire enable gpio\n");
5490cbbd5b1SDmitry Baryshkov return PTR_ERR(lt9611uxc->enable_gpio);
5500cbbd5b1SDmitry Baryshkov }
5510cbbd5b1SDmitry Baryshkov
5520cbbd5b1SDmitry Baryshkov return 0;
5530cbbd5b1SDmitry Baryshkov }
5540cbbd5b1SDmitry Baryshkov
lt9611uxc_read_device_rev(struct lt9611uxc * lt9611uxc)5550cbbd5b1SDmitry Baryshkov static int lt9611uxc_read_device_rev(struct lt9611uxc *lt9611uxc)
5560cbbd5b1SDmitry Baryshkov {
5570cbbd5b1SDmitry Baryshkov unsigned int rev0, rev1, rev2;
5580cbbd5b1SDmitry Baryshkov int ret;
5590cbbd5b1SDmitry Baryshkov
5600cbbd5b1SDmitry Baryshkov lt9611uxc_lock(lt9611uxc);
5610cbbd5b1SDmitry Baryshkov
5620cbbd5b1SDmitry Baryshkov ret = regmap_read(lt9611uxc->regmap, 0x8100, &rev0);
5630cbbd5b1SDmitry Baryshkov ret |= regmap_read(lt9611uxc->regmap, 0x8101, &rev1);
5640cbbd5b1SDmitry Baryshkov ret |= regmap_read(lt9611uxc->regmap, 0x8102, &rev2);
5650cbbd5b1SDmitry Baryshkov if (ret)
5660cbbd5b1SDmitry Baryshkov dev_err(lt9611uxc->dev, "failed to read revision: %d\n", ret);
5670cbbd5b1SDmitry Baryshkov else
5680cbbd5b1SDmitry Baryshkov dev_info(lt9611uxc->dev, "LT9611 revision: 0x%02x.%02x.%02x\n", rev0, rev1, rev2);
5690cbbd5b1SDmitry Baryshkov
5700cbbd5b1SDmitry Baryshkov lt9611uxc_unlock(lt9611uxc);
5710cbbd5b1SDmitry Baryshkov
5720cbbd5b1SDmitry Baryshkov return ret;
5730cbbd5b1SDmitry Baryshkov }
5740cbbd5b1SDmitry Baryshkov
lt9611uxc_read_version(struct lt9611uxc * lt9611uxc)5750cbbd5b1SDmitry Baryshkov static int lt9611uxc_read_version(struct lt9611uxc *lt9611uxc)
5760cbbd5b1SDmitry Baryshkov {
5770cbbd5b1SDmitry Baryshkov unsigned int rev;
5780cbbd5b1SDmitry Baryshkov int ret;
5790cbbd5b1SDmitry Baryshkov
5800cbbd5b1SDmitry Baryshkov lt9611uxc_lock(lt9611uxc);
5810cbbd5b1SDmitry Baryshkov
5820cbbd5b1SDmitry Baryshkov ret = regmap_read(lt9611uxc->regmap, 0xb021, &rev);
5830cbbd5b1SDmitry Baryshkov if (ret)
5840cbbd5b1SDmitry Baryshkov dev_err(lt9611uxc->dev, "failed to read revision: %d\n", ret);
5850cbbd5b1SDmitry Baryshkov else
5860cbbd5b1SDmitry Baryshkov dev_info(lt9611uxc->dev, "LT9611 version: 0x%02x\n", rev);
5870cbbd5b1SDmitry Baryshkov
5880cbbd5b1SDmitry Baryshkov lt9611uxc_unlock(lt9611uxc);
5890cbbd5b1SDmitry Baryshkov
5900cbbd5b1SDmitry Baryshkov return ret < 0 ? ret : rev;
5910cbbd5b1SDmitry Baryshkov }
5920cbbd5b1SDmitry Baryshkov
lt9611uxc_hdmi_hw_params(struct device * dev,void * data,struct hdmi_codec_daifmt * fmt,struct hdmi_codec_params * hparms)5930cbbd5b1SDmitry Baryshkov static int lt9611uxc_hdmi_hw_params(struct device *dev, void *data,
5940cbbd5b1SDmitry Baryshkov struct hdmi_codec_daifmt *fmt,
5950cbbd5b1SDmitry Baryshkov struct hdmi_codec_params *hparms)
5960cbbd5b1SDmitry Baryshkov {
5970cbbd5b1SDmitry Baryshkov /*
5980cbbd5b1SDmitry Baryshkov * LT9611UXC will automatically detect rate and sample size, so no need
5990cbbd5b1SDmitry Baryshkov * to setup anything here.
6000cbbd5b1SDmitry Baryshkov */
6010cbbd5b1SDmitry Baryshkov return 0;
6020cbbd5b1SDmitry Baryshkov }
6030cbbd5b1SDmitry Baryshkov
lt9611uxc_audio_shutdown(struct device * dev,void * data)6040cbbd5b1SDmitry Baryshkov static void lt9611uxc_audio_shutdown(struct device *dev, void *data)
6050cbbd5b1SDmitry Baryshkov {
6060cbbd5b1SDmitry Baryshkov }
6070cbbd5b1SDmitry Baryshkov
lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component * component,struct device_node * endpoint)6080cbbd5b1SDmitry Baryshkov static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
6090cbbd5b1SDmitry Baryshkov struct device_node *endpoint)
6100cbbd5b1SDmitry Baryshkov {
6110cbbd5b1SDmitry Baryshkov struct of_endpoint of_ep;
6120cbbd5b1SDmitry Baryshkov int ret;
6130cbbd5b1SDmitry Baryshkov
6140cbbd5b1SDmitry Baryshkov ret = of_graph_parse_endpoint(endpoint, &of_ep);
6150cbbd5b1SDmitry Baryshkov if (ret < 0)
6160cbbd5b1SDmitry Baryshkov return ret;
6170cbbd5b1SDmitry Baryshkov
6180cbbd5b1SDmitry Baryshkov /*
6190cbbd5b1SDmitry Baryshkov * HDMI sound should be located as reg = <2>
6200cbbd5b1SDmitry Baryshkov * Then, it is sound port 0
6210cbbd5b1SDmitry Baryshkov */
6220cbbd5b1SDmitry Baryshkov if (of_ep.port == 2)
6230cbbd5b1SDmitry Baryshkov return 0;
6240cbbd5b1SDmitry Baryshkov
6250cbbd5b1SDmitry Baryshkov return -EINVAL;
6260cbbd5b1SDmitry Baryshkov }
6270cbbd5b1SDmitry Baryshkov
6280cbbd5b1SDmitry Baryshkov static const struct hdmi_codec_ops lt9611uxc_codec_ops = {
6290cbbd5b1SDmitry Baryshkov .hw_params = lt9611uxc_hdmi_hw_params,
6300cbbd5b1SDmitry Baryshkov .audio_shutdown = lt9611uxc_audio_shutdown,
6310cbbd5b1SDmitry Baryshkov .get_dai_id = lt9611uxc_hdmi_i2s_get_dai_id,
6320cbbd5b1SDmitry Baryshkov };
6330cbbd5b1SDmitry Baryshkov
lt9611uxc_audio_init(struct device * dev,struct lt9611uxc * lt9611uxc)6340cbbd5b1SDmitry Baryshkov static int lt9611uxc_audio_init(struct device *dev, struct lt9611uxc *lt9611uxc)
6350cbbd5b1SDmitry Baryshkov {
6360cbbd5b1SDmitry Baryshkov struct hdmi_codec_pdata codec_data = {
6370cbbd5b1SDmitry Baryshkov .ops = <9611uxc_codec_ops,
6380cbbd5b1SDmitry Baryshkov .max_i2s_channels = 2,
6390cbbd5b1SDmitry Baryshkov .i2s = 1,
6400cbbd5b1SDmitry Baryshkov .data = lt9611uxc,
6410cbbd5b1SDmitry Baryshkov };
6420cbbd5b1SDmitry Baryshkov
6430cbbd5b1SDmitry Baryshkov lt9611uxc->audio_pdev =
6440cbbd5b1SDmitry Baryshkov platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
6450cbbd5b1SDmitry Baryshkov PLATFORM_DEVID_AUTO,
6460cbbd5b1SDmitry Baryshkov &codec_data, sizeof(codec_data));
6470cbbd5b1SDmitry Baryshkov
6480cbbd5b1SDmitry Baryshkov return PTR_ERR_OR_ZERO(lt9611uxc->audio_pdev);
6490cbbd5b1SDmitry Baryshkov }
6500cbbd5b1SDmitry Baryshkov
lt9611uxc_audio_exit(struct lt9611uxc * lt9611uxc)6510cbbd5b1SDmitry Baryshkov static void lt9611uxc_audio_exit(struct lt9611uxc *lt9611uxc)
6520cbbd5b1SDmitry Baryshkov {
6530cbbd5b1SDmitry Baryshkov if (lt9611uxc->audio_pdev) {
6540cbbd5b1SDmitry Baryshkov platform_device_unregister(lt9611uxc->audio_pdev);
6550cbbd5b1SDmitry Baryshkov lt9611uxc->audio_pdev = NULL;
6560cbbd5b1SDmitry Baryshkov }
6570cbbd5b1SDmitry Baryshkov }
6580cbbd5b1SDmitry Baryshkov
6590cbbd5b1SDmitry Baryshkov #define LT9611UXC_FW_PAGE_SIZE 32
lt9611uxc_firmware_write_page(struct lt9611uxc * lt9611uxc,u16 addr,const u8 * buf)6600cbbd5b1SDmitry Baryshkov static void lt9611uxc_firmware_write_page(struct lt9611uxc *lt9611uxc, u16 addr, const u8 *buf)
6610cbbd5b1SDmitry Baryshkov {
6620cbbd5b1SDmitry Baryshkov struct reg_sequence seq_write_prepare[] = {
6630cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x04),
6640cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x00),
6650cbbd5b1SDmitry Baryshkov
6660cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805e, 0xdf),
6670cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x20),
6680cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x00),
6690cbbd5b1SDmitry Baryshkov REG_SEQ0(0x8058, 0x21),
6700cbbd5b1SDmitry Baryshkov };
6710cbbd5b1SDmitry Baryshkov
6720cbbd5b1SDmitry Baryshkov struct reg_sequence seq_write_addr[] = {
6730cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805b, (addr >> 16) & 0xff),
6740cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805c, (addr >> 8) & 0xff),
6750cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805d, addr & 0xff),
6760cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x10),
6770cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x00),
6780cbbd5b1SDmitry Baryshkov };
6790cbbd5b1SDmitry Baryshkov
6800cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0x8108, 0xbf);
6810cbbd5b1SDmitry Baryshkov msleep(20);
6820cbbd5b1SDmitry Baryshkov regmap_write(lt9611uxc->regmap, 0x8108, 0xff);
6830cbbd5b1SDmitry Baryshkov msleep(20);
6840cbbd5b1SDmitry Baryshkov regmap_multi_reg_write(lt9611uxc->regmap, seq_write_prepare, ARRAY_SIZE(seq_write_prepare));
6850cbbd5b1SDmitry Baryshkov regmap_noinc_write(lt9611uxc->regmap, 0x8059, buf, LT9611UXC_FW_PAGE_SIZE);
6860cbbd5b1SDmitry Baryshkov regmap_multi_reg_write(lt9611uxc->regmap, seq_write_addr, ARRAY_SIZE(seq_write_addr));
6870cbbd5b1SDmitry Baryshkov msleep(20);
6880cbbd5b1SDmitry Baryshkov }
6890cbbd5b1SDmitry Baryshkov
lt9611uxc_firmware_read_page(struct lt9611uxc * lt9611uxc,u16 addr,char * buf)6900cbbd5b1SDmitry Baryshkov static void lt9611uxc_firmware_read_page(struct lt9611uxc *lt9611uxc, u16 addr, char *buf)
6910cbbd5b1SDmitry Baryshkov {
6920cbbd5b1SDmitry Baryshkov struct reg_sequence seq_read_page[] = {
6930cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0xa0),
6940cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x80),
6950cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805b, (addr >> 16) & 0xff),
6960cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805c, (addr >> 8) & 0xff),
6970cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805d, addr & 0xff),
6980cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x90),
6990cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x80),
7000cbbd5b1SDmitry Baryshkov REG_SEQ0(0x8058, 0x21),
7010cbbd5b1SDmitry Baryshkov };
7020cbbd5b1SDmitry Baryshkov
7030cbbd5b1SDmitry Baryshkov regmap_multi_reg_write(lt9611uxc->regmap, seq_read_page, ARRAY_SIZE(seq_read_page));
7040cbbd5b1SDmitry Baryshkov regmap_noinc_read(lt9611uxc->regmap, 0x805f, buf, LT9611UXC_FW_PAGE_SIZE);
7050cbbd5b1SDmitry Baryshkov }
7060cbbd5b1SDmitry Baryshkov
lt9611uxc_firmware_read(struct lt9611uxc * lt9611uxc,size_t size)7070cbbd5b1SDmitry Baryshkov static char *lt9611uxc_firmware_read(struct lt9611uxc *lt9611uxc, size_t size)
7080cbbd5b1SDmitry Baryshkov {
7090cbbd5b1SDmitry Baryshkov struct reg_sequence seq_read_setup[] = {
7100cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x84),
7110cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x80),
7120cbbd5b1SDmitry Baryshkov };
7130cbbd5b1SDmitry Baryshkov
7140cbbd5b1SDmitry Baryshkov char *readbuf;
7150cbbd5b1SDmitry Baryshkov u16 offset;
7160cbbd5b1SDmitry Baryshkov
7170cbbd5b1SDmitry Baryshkov readbuf = kzalloc(ALIGN(size, 32), GFP_KERNEL);
7180cbbd5b1SDmitry Baryshkov if (!readbuf)
7190cbbd5b1SDmitry Baryshkov return NULL;
7200cbbd5b1SDmitry Baryshkov
7210cbbd5b1SDmitry Baryshkov regmap_multi_reg_write(lt9611uxc->regmap, seq_read_setup, ARRAY_SIZE(seq_read_setup));
7220cbbd5b1SDmitry Baryshkov
7230cbbd5b1SDmitry Baryshkov for (offset = 0;
7240cbbd5b1SDmitry Baryshkov offset < size;
7250cbbd5b1SDmitry Baryshkov offset += LT9611UXC_FW_PAGE_SIZE)
7260cbbd5b1SDmitry Baryshkov lt9611uxc_firmware_read_page(lt9611uxc, offset, &readbuf[offset]);
7270cbbd5b1SDmitry Baryshkov
7280cbbd5b1SDmitry Baryshkov return readbuf;
7290cbbd5b1SDmitry Baryshkov }
7300cbbd5b1SDmitry Baryshkov
lt9611uxc_firmware_update(struct lt9611uxc * lt9611uxc)7310cbbd5b1SDmitry Baryshkov static int lt9611uxc_firmware_update(struct lt9611uxc *lt9611uxc)
7320cbbd5b1SDmitry Baryshkov {
7330cbbd5b1SDmitry Baryshkov int ret;
7340cbbd5b1SDmitry Baryshkov u16 offset;
7350cbbd5b1SDmitry Baryshkov size_t remain;
7360cbbd5b1SDmitry Baryshkov char *readbuf;
7370cbbd5b1SDmitry Baryshkov const struct firmware *fw;
7380cbbd5b1SDmitry Baryshkov
7390cbbd5b1SDmitry Baryshkov struct reg_sequence seq_setup[] = {
7400cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805e, 0xdf),
7410cbbd5b1SDmitry Baryshkov REG_SEQ0(0x8058, 0x00),
7420cbbd5b1SDmitry Baryshkov REG_SEQ0(0x8059, 0x50),
7430cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x10),
7440cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x00),
7450cbbd5b1SDmitry Baryshkov };
7460cbbd5b1SDmitry Baryshkov
7470cbbd5b1SDmitry Baryshkov
7480cbbd5b1SDmitry Baryshkov struct reg_sequence seq_block_erase[] = {
7490cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x04),
7500cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x00),
7510cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805b, 0x00),
7520cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805c, 0x00),
7530cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805d, 0x00),
7540cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x01),
7550cbbd5b1SDmitry Baryshkov REG_SEQ0(0x805a, 0x00),
7560cbbd5b1SDmitry Baryshkov };
7570cbbd5b1SDmitry Baryshkov
758354c0fb6SJuerg Haefliger ret = request_firmware(&fw, FW_FILE, lt9611uxc->dev);
7590cbbd5b1SDmitry Baryshkov if (ret < 0)
7600cbbd5b1SDmitry Baryshkov return ret;
7610cbbd5b1SDmitry Baryshkov
7620cbbd5b1SDmitry Baryshkov dev_info(lt9611uxc->dev, "Updating firmware\n");
7630cbbd5b1SDmitry Baryshkov lt9611uxc_lock(lt9611uxc);
7640cbbd5b1SDmitry Baryshkov
7650cbbd5b1SDmitry Baryshkov regmap_multi_reg_write(lt9611uxc->regmap, seq_setup, ARRAY_SIZE(seq_setup));
7660cbbd5b1SDmitry Baryshkov
7670cbbd5b1SDmitry Baryshkov /*
7680cbbd5b1SDmitry Baryshkov * Need erase block 2 timess here. Sometimes, block erase can fail.
7690cbbd5b1SDmitry Baryshkov * This is a workaroud.
7700cbbd5b1SDmitry Baryshkov */
7710cbbd5b1SDmitry Baryshkov regmap_multi_reg_write(lt9611uxc->regmap, seq_block_erase, ARRAY_SIZE(seq_block_erase));
7720cbbd5b1SDmitry Baryshkov msleep(3000);
7730cbbd5b1SDmitry Baryshkov regmap_multi_reg_write(lt9611uxc->regmap, seq_block_erase, ARRAY_SIZE(seq_block_erase));
7740cbbd5b1SDmitry Baryshkov msleep(3000);
7750cbbd5b1SDmitry Baryshkov
7760cbbd5b1SDmitry Baryshkov for (offset = 0, remain = fw->size;
7770cbbd5b1SDmitry Baryshkov remain >= LT9611UXC_FW_PAGE_SIZE;
7780cbbd5b1SDmitry Baryshkov offset += LT9611UXC_FW_PAGE_SIZE, remain -= LT9611UXC_FW_PAGE_SIZE)
7790cbbd5b1SDmitry Baryshkov lt9611uxc_firmware_write_page(lt9611uxc, offset, fw->data + offset);
7800cbbd5b1SDmitry Baryshkov
7810cbbd5b1SDmitry Baryshkov if (remain > 0) {
7820cbbd5b1SDmitry Baryshkov char buf[LT9611UXC_FW_PAGE_SIZE];
7830cbbd5b1SDmitry Baryshkov
7840cbbd5b1SDmitry Baryshkov memset(buf, 0xff, LT9611UXC_FW_PAGE_SIZE);
7850cbbd5b1SDmitry Baryshkov memcpy(buf, fw->data + offset, remain);
7860cbbd5b1SDmitry Baryshkov lt9611uxc_firmware_write_page(lt9611uxc, offset, buf);
7870cbbd5b1SDmitry Baryshkov }
7880cbbd5b1SDmitry Baryshkov msleep(20);
7890cbbd5b1SDmitry Baryshkov
7900cbbd5b1SDmitry Baryshkov readbuf = lt9611uxc_firmware_read(lt9611uxc, fw->size);
7910cbbd5b1SDmitry Baryshkov if (!readbuf) {
7920cbbd5b1SDmitry Baryshkov ret = -ENOMEM;
7930cbbd5b1SDmitry Baryshkov goto out;
7940cbbd5b1SDmitry Baryshkov }
7950cbbd5b1SDmitry Baryshkov
7960cbbd5b1SDmitry Baryshkov if (!memcmp(readbuf, fw->data, fw->size)) {
7970cbbd5b1SDmitry Baryshkov dev_err(lt9611uxc->dev, "Firmware update failed\n");
7980cbbd5b1SDmitry Baryshkov print_hex_dump(KERN_ERR, "fw: ", DUMP_PREFIX_OFFSET, 16, 1, readbuf, fw->size, false);
7990cbbd5b1SDmitry Baryshkov ret = -EINVAL;
8000cbbd5b1SDmitry Baryshkov } else {
8010cbbd5b1SDmitry Baryshkov dev_info(lt9611uxc->dev, "Firmware updates successfully\n");
8020cbbd5b1SDmitry Baryshkov ret = 0;
8030cbbd5b1SDmitry Baryshkov }
8040cbbd5b1SDmitry Baryshkov kfree(readbuf);
8050cbbd5b1SDmitry Baryshkov
8060cbbd5b1SDmitry Baryshkov out:
8070cbbd5b1SDmitry Baryshkov lt9611uxc_unlock(lt9611uxc);
8080cbbd5b1SDmitry Baryshkov lt9611uxc_reset(lt9611uxc);
8090cbbd5b1SDmitry Baryshkov release_firmware(fw);
8100cbbd5b1SDmitry Baryshkov
8110cbbd5b1SDmitry Baryshkov return ret;
8120cbbd5b1SDmitry Baryshkov }
8130cbbd5b1SDmitry Baryshkov
lt9611uxc_firmware_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)8140cbbd5b1SDmitry Baryshkov static ssize_t lt9611uxc_firmware_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
8150cbbd5b1SDmitry Baryshkov {
8160cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = dev_get_drvdata(dev);
8170cbbd5b1SDmitry Baryshkov int ret;
8180cbbd5b1SDmitry Baryshkov
8190cbbd5b1SDmitry Baryshkov ret = lt9611uxc_firmware_update(lt9611uxc);
8200cbbd5b1SDmitry Baryshkov if (ret < 0)
8210cbbd5b1SDmitry Baryshkov return ret;
8220cbbd5b1SDmitry Baryshkov return len;
8230cbbd5b1SDmitry Baryshkov }
8240cbbd5b1SDmitry Baryshkov
lt9611uxc_firmware_show(struct device * dev,struct device_attribute * attr,char * buf)8250cbbd5b1SDmitry Baryshkov static ssize_t lt9611uxc_firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
8260cbbd5b1SDmitry Baryshkov {
8270cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = dev_get_drvdata(dev);
8280cbbd5b1SDmitry Baryshkov
829fffa69aaSJiapeng Chong return sysfs_emit(buf, "%02x\n", lt9611uxc->fw_version);
8300cbbd5b1SDmitry Baryshkov }
8310cbbd5b1SDmitry Baryshkov
8320cbbd5b1SDmitry Baryshkov static DEVICE_ATTR_RW(lt9611uxc_firmware);
8330cbbd5b1SDmitry Baryshkov
8340cbbd5b1SDmitry Baryshkov static struct attribute *lt9611uxc_attrs[] = {
8350cbbd5b1SDmitry Baryshkov &dev_attr_lt9611uxc_firmware.attr,
8360cbbd5b1SDmitry Baryshkov NULL,
8370cbbd5b1SDmitry Baryshkov };
8380cbbd5b1SDmitry Baryshkov
8390cbbd5b1SDmitry Baryshkov static const struct attribute_group lt9611uxc_attr_group = {
8400cbbd5b1SDmitry Baryshkov .attrs = lt9611uxc_attrs,
8410cbbd5b1SDmitry Baryshkov };
8420cbbd5b1SDmitry Baryshkov
8430cbbd5b1SDmitry Baryshkov static const struct attribute_group *lt9611uxc_attr_groups[] = {
8440cbbd5b1SDmitry Baryshkov <9611uxc_attr_group,
8450cbbd5b1SDmitry Baryshkov NULL,
8460cbbd5b1SDmitry Baryshkov };
8470cbbd5b1SDmitry Baryshkov
lt9611uxc_probe(struct i2c_client * client)848cae75557SUwe Kleine-König static int lt9611uxc_probe(struct i2c_client *client)
8490cbbd5b1SDmitry Baryshkov {
8500cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc;
8510cbbd5b1SDmitry Baryshkov struct device *dev = &client->dev;
8520cbbd5b1SDmitry Baryshkov int ret;
8530cbbd5b1SDmitry Baryshkov bool fw_updated = false;
8540cbbd5b1SDmitry Baryshkov
8550cbbd5b1SDmitry Baryshkov if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
8560cbbd5b1SDmitry Baryshkov dev_err(dev, "device doesn't support I2C\n");
8570cbbd5b1SDmitry Baryshkov return -ENODEV;
8580cbbd5b1SDmitry Baryshkov }
8590cbbd5b1SDmitry Baryshkov
8600cbbd5b1SDmitry Baryshkov lt9611uxc = devm_kzalloc(dev, sizeof(*lt9611uxc), GFP_KERNEL);
8610cbbd5b1SDmitry Baryshkov if (!lt9611uxc)
8620cbbd5b1SDmitry Baryshkov return -ENOMEM;
8630cbbd5b1SDmitry Baryshkov
864cc37b88bSZhiming Liu lt9611uxc->dev = dev;
8650cbbd5b1SDmitry Baryshkov lt9611uxc->client = client;
8660cbbd5b1SDmitry Baryshkov mutex_init(<9611uxc->ocm_lock);
8670cbbd5b1SDmitry Baryshkov
8680cbbd5b1SDmitry Baryshkov lt9611uxc->regmap = devm_regmap_init_i2c(client, <9611uxc_regmap_config);
8690cbbd5b1SDmitry Baryshkov if (IS_ERR(lt9611uxc->regmap)) {
8700cbbd5b1SDmitry Baryshkov dev_err(lt9611uxc->dev, "regmap i2c init failed\n");
8710cbbd5b1SDmitry Baryshkov return PTR_ERR(lt9611uxc->regmap);
8720cbbd5b1SDmitry Baryshkov }
8730cbbd5b1SDmitry Baryshkov
874cc37b88bSZhiming Liu ret = lt9611uxc_parse_dt(dev, lt9611uxc);
8750cbbd5b1SDmitry Baryshkov if (ret) {
8760cbbd5b1SDmitry Baryshkov dev_err(dev, "failed to parse device tree\n");
8770cbbd5b1SDmitry Baryshkov return ret;
8780cbbd5b1SDmitry Baryshkov }
8790cbbd5b1SDmitry Baryshkov
8800cbbd5b1SDmitry Baryshkov ret = lt9611uxc_gpio_init(lt9611uxc);
8810cbbd5b1SDmitry Baryshkov if (ret < 0)
8820cbbd5b1SDmitry Baryshkov goto err_of_put;
8830cbbd5b1SDmitry Baryshkov
8840cbbd5b1SDmitry Baryshkov ret = lt9611uxc_regulator_init(lt9611uxc);
8850cbbd5b1SDmitry Baryshkov if (ret < 0)
8860cbbd5b1SDmitry Baryshkov goto err_of_put;
8870cbbd5b1SDmitry Baryshkov
8880cbbd5b1SDmitry Baryshkov lt9611uxc_assert_5v(lt9611uxc);
8890cbbd5b1SDmitry Baryshkov
8900cbbd5b1SDmitry Baryshkov ret = lt9611uxc_regulator_enable(lt9611uxc);
8910cbbd5b1SDmitry Baryshkov if (ret)
8920cbbd5b1SDmitry Baryshkov goto err_of_put;
8930cbbd5b1SDmitry Baryshkov
8940cbbd5b1SDmitry Baryshkov lt9611uxc_reset(lt9611uxc);
8950cbbd5b1SDmitry Baryshkov
8960cbbd5b1SDmitry Baryshkov ret = lt9611uxc_read_device_rev(lt9611uxc);
8970cbbd5b1SDmitry Baryshkov if (ret) {
8980cbbd5b1SDmitry Baryshkov dev_err(dev, "failed to read chip rev\n");
8990cbbd5b1SDmitry Baryshkov goto err_disable_regulators;
9000cbbd5b1SDmitry Baryshkov }
9010cbbd5b1SDmitry Baryshkov
9020cbbd5b1SDmitry Baryshkov retry:
9030cbbd5b1SDmitry Baryshkov ret = lt9611uxc_read_version(lt9611uxc);
9040cbbd5b1SDmitry Baryshkov if (ret < 0) {
9050cbbd5b1SDmitry Baryshkov dev_err(dev, "failed to read FW version\n");
9060cbbd5b1SDmitry Baryshkov goto err_disable_regulators;
9070cbbd5b1SDmitry Baryshkov } else if (ret == 0) {
9080cbbd5b1SDmitry Baryshkov if (!fw_updated) {
9090cbbd5b1SDmitry Baryshkov fw_updated = true;
9100cbbd5b1SDmitry Baryshkov dev_err(dev, "FW version 0, enforcing firmware update\n");
9110cbbd5b1SDmitry Baryshkov ret = lt9611uxc_firmware_update(lt9611uxc);
9120cbbd5b1SDmitry Baryshkov if (ret < 0)
9130cbbd5b1SDmitry Baryshkov goto err_disable_regulators;
9140cbbd5b1SDmitry Baryshkov else
9150cbbd5b1SDmitry Baryshkov goto retry;
9160cbbd5b1SDmitry Baryshkov } else {
9170cbbd5b1SDmitry Baryshkov dev_err(dev, "FW version 0, update failed\n");
9180cbbd5b1SDmitry Baryshkov ret = -EOPNOTSUPP;
9190cbbd5b1SDmitry Baryshkov goto err_disable_regulators;
9200cbbd5b1SDmitry Baryshkov }
9210cbbd5b1SDmitry Baryshkov } else if (ret < 0x40) {
9220cbbd5b1SDmitry Baryshkov dev_info(dev, "FW version 0x%x, HPD not supported\n", ret);
9230cbbd5b1SDmitry Baryshkov } else {
9240cbbd5b1SDmitry Baryshkov lt9611uxc->hpd_supported = true;
9250cbbd5b1SDmitry Baryshkov }
9260cbbd5b1SDmitry Baryshkov lt9611uxc->fw_version = ret;
9270cbbd5b1SDmitry Baryshkov
9280cbbd5b1SDmitry Baryshkov init_waitqueue_head(<9611uxc->wq);
929bc6fa867SDmitry Baryshkov INIT_WORK(<9611uxc->work, lt9611uxc_hpd_work);
930bc6fa867SDmitry Baryshkov
93115fe53beSDmitry Baryshkov ret = request_threaded_irq(client->irq, NULL,
9320cbbd5b1SDmitry Baryshkov lt9611uxc_irq_thread_handler,
9330cbbd5b1SDmitry Baryshkov IRQF_ONESHOT, "lt9611uxc", lt9611uxc);
9340cbbd5b1SDmitry Baryshkov if (ret) {
9350cbbd5b1SDmitry Baryshkov dev_err(dev, "failed to request irq\n");
9360cbbd5b1SDmitry Baryshkov goto err_disable_regulators;
9370cbbd5b1SDmitry Baryshkov }
9380cbbd5b1SDmitry Baryshkov
9390cbbd5b1SDmitry Baryshkov i2c_set_clientdata(client, lt9611uxc);
9400cbbd5b1SDmitry Baryshkov
9410cbbd5b1SDmitry Baryshkov lt9611uxc->bridge.funcs = <9611uxc_bridge_funcs;
9420cbbd5b1SDmitry Baryshkov lt9611uxc->bridge.of_node = client->dev.of_node;
9430cbbd5b1SDmitry Baryshkov lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
9440cbbd5b1SDmitry Baryshkov if (lt9611uxc->hpd_supported)
9450cbbd5b1SDmitry Baryshkov lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_HPD;
9460cbbd5b1SDmitry Baryshkov lt9611uxc->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
9470cbbd5b1SDmitry Baryshkov
9480cbbd5b1SDmitry Baryshkov drm_bridge_add(<9611uxc->bridge);
9490cbbd5b1SDmitry Baryshkov
9504a46ace5SMaxime Ripard /* Attach primary DSI */
9514a46ace5SMaxime Ripard lt9611uxc->dsi0 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi0_node);
9524a46ace5SMaxime Ripard if (IS_ERR(lt9611uxc->dsi0)) {
9534a46ace5SMaxime Ripard ret = PTR_ERR(lt9611uxc->dsi0);
9544a46ace5SMaxime Ripard goto err_remove_bridge;
9554a46ace5SMaxime Ripard }
9564a46ace5SMaxime Ripard
9574a46ace5SMaxime Ripard /* Attach secondary DSI, if specified */
9584a46ace5SMaxime Ripard if (lt9611uxc->dsi1_node) {
9594a46ace5SMaxime Ripard lt9611uxc->dsi1 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi1_node);
9604a46ace5SMaxime Ripard if (IS_ERR(lt9611uxc->dsi1)) {
9614a46ace5SMaxime Ripard ret = PTR_ERR(lt9611uxc->dsi1);
9624a46ace5SMaxime Ripard goto err_remove_bridge;
9634a46ace5SMaxime Ripard }
9644a46ace5SMaxime Ripard }
9654a46ace5SMaxime Ripard
9660cbbd5b1SDmitry Baryshkov return lt9611uxc_audio_init(dev, lt9611uxc);
9670cbbd5b1SDmitry Baryshkov
9684a46ace5SMaxime Ripard err_remove_bridge:
96915fe53beSDmitry Baryshkov free_irq(client->irq, lt9611uxc);
97015fe53beSDmitry Baryshkov cancel_work_sync(<9611uxc->work);
9714a46ace5SMaxime Ripard drm_bridge_remove(<9611uxc->bridge);
9724a46ace5SMaxime Ripard
9730cbbd5b1SDmitry Baryshkov err_disable_regulators:
9740cbbd5b1SDmitry Baryshkov regulator_bulk_disable(ARRAY_SIZE(lt9611uxc->supplies), lt9611uxc->supplies);
9750cbbd5b1SDmitry Baryshkov
9760cbbd5b1SDmitry Baryshkov err_of_put:
9770cbbd5b1SDmitry Baryshkov of_node_put(lt9611uxc->dsi1_node);
9780cbbd5b1SDmitry Baryshkov of_node_put(lt9611uxc->dsi0_node);
9790cbbd5b1SDmitry Baryshkov
9800cbbd5b1SDmitry Baryshkov return ret;
9810cbbd5b1SDmitry Baryshkov }
9820cbbd5b1SDmitry Baryshkov
lt9611uxc_remove(struct i2c_client * client)983ed5c2f5fSUwe Kleine-König static void lt9611uxc_remove(struct i2c_client *client)
9840cbbd5b1SDmitry Baryshkov {
9850cbbd5b1SDmitry Baryshkov struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client);
9860cbbd5b1SDmitry Baryshkov
98715fe53beSDmitry Baryshkov free_irq(client->irq, lt9611uxc);
988dfa687bfSBjorn Andersson cancel_work_sync(<9611uxc->work);
9890cbbd5b1SDmitry Baryshkov lt9611uxc_audio_exit(lt9611uxc);
9900cbbd5b1SDmitry Baryshkov drm_bridge_remove(<9611uxc->bridge);
9910cbbd5b1SDmitry Baryshkov
9920cbbd5b1SDmitry Baryshkov mutex_destroy(<9611uxc->ocm_lock);
9930cbbd5b1SDmitry Baryshkov
9940cbbd5b1SDmitry Baryshkov regulator_bulk_disable(ARRAY_SIZE(lt9611uxc->supplies), lt9611uxc->supplies);
9950cbbd5b1SDmitry Baryshkov
9960cbbd5b1SDmitry Baryshkov of_node_put(lt9611uxc->dsi1_node);
9970cbbd5b1SDmitry Baryshkov of_node_put(lt9611uxc->dsi0_node);
9980cbbd5b1SDmitry Baryshkov }
9990cbbd5b1SDmitry Baryshkov
10000cbbd5b1SDmitry Baryshkov static struct i2c_device_id lt9611uxc_id[] = {
10010cbbd5b1SDmitry Baryshkov { "lontium,lt9611uxc", 0 },
10020cbbd5b1SDmitry Baryshkov { /* sentinel */ }
10030cbbd5b1SDmitry Baryshkov };
10040cbbd5b1SDmitry Baryshkov
10050cbbd5b1SDmitry Baryshkov static const struct of_device_id lt9611uxc_match_table[] = {
10060cbbd5b1SDmitry Baryshkov { .compatible = "lontium,lt9611uxc" },
10070cbbd5b1SDmitry Baryshkov { /* sentinel */ }
10080cbbd5b1SDmitry Baryshkov };
10090cbbd5b1SDmitry Baryshkov MODULE_DEVICE_TABLE(of, lt9611uxc_match_table);
10100cbbd5b1SDmitry Baryshkov
10110cbbd5b1SDmitry Baryshkov static struct i2c_driver lt9611uxc_driver = {
10120cbbd5b1SDmitry Baryshkov .driver = {
10130cbbd5b1SDmitry Baryshkov .name = "lt9611uxc",
10140cbbd5b1SDmitry Baryshkov .of_match_table = lt9611uxc_match_table,
10150cbbd5b1SDmitry Baryshkov .dev_groups = lt9611uxc_attr_groups,
10160cbbd5b1SDmitry Baryshkov },
1017332af828SUwe Kleine-König .probe = lt9611uxc_probe,
10180cbbd5b1SDmitry Baryshkov .remove = lt9611uxc_remove,
10190cbbd5b1SDmitry Baryshkov .id_table = lt9611uxc_id,
10200cbbd5b1SDmitry Baryshkov };
10210cbbd5b1SDmitry Baryshkov module_i2c_driver(lt9611uxc_driver);
10220cbbd5b1SDmitry Baryshkov
10230cbbd5b1SDmitry Baryshkov MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
10240cbbd5b1SDmitry Baryshkov MODULE_LICENSE("GPL v2");
1025354c0fb6SJuerg Haefliger
1026354c0fb6SJuerg Haefliger MODULE_FIRMWARE(FW_FILE);
1027