18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Texas Instruments
48c2ecf20Sopenharmony_ci * Author: Jyri Sarha <jsarha@ti.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
88c2ecf20Sopenharmony_ci#include <linux/i2c.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/of_graph.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define HOTPLUG_DEBOUNCE_MS		1100
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct tfp410 {
238c2ecf20Sopenharmony_ci	struct drm_bridge	bridge;
248c2ecf20Sopenharmony_ci	struct drm_connector	connector;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	u32			bus_format;
278c2ecf20Sopenharmony_ci	struct delayed_work	hpd_work;
288c2ecf20Sopenharmony_ci	struct gpio_desc	*powerdown;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	struct drm_bridge_timings timings;
318c2ecf20Sopenharmony_ci	struct drm_bridge	*next_bridge;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	struct device *dev;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic inline struct tfp410 *
378c2ecf20Sopenharmony_cidrm_bridge_to_tfp410(struct drm_bridge *bridge)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	return container_of(bridge, struct tfp410, bridge);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic inline struct tfp410 *
438c2ecf20Sopenharmony_cidrm_connector_to_tfp410(struct drm_connector *connector)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	return container_of(connector, struct tfp410, connector);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int tfp410_get_modes(struct drm_connector *connector)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct tfp410 *dvi = drm_connector_to_tfp410(connector);
518c2ecf20Sopenharmony_ci	struct edid *edid;
528c2ecf20Sopenharmony_ci	int ret;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (dvi->next_bridge->ops & DRM_BRIDGE_OP_EDID) {
558c2ecf20Sopenharmony_ci		edid = drm_bridge_get_edid(dvi->next_bridge, connector);
568c2ecf20Sopenharmony_ci		if (!edid)
578c2ecf20Sopenharmony_ci			DRM_INFO("EDID read failed. Fallback to standard modes\n");
588c2ecf20Sopenharmony_ci	} else {
598c2ecf20Sopenharmony_ci		edid = NULL;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!edid) {
638c2ecf20Sopenharmony_ci		/*
648c2ecf20Sopenharmony_ci		 * No EDID, fallback on the XGA standard modes and prefer a mode
658c2ecf20Sopenharmony_ci		 * pretty much anything can handle.
668c2ecf20Sopenharmony_ci		 */
678c2ecf20Sopenharmony_ci		ret = drm_add_modes_noedid(connector, 1920, 1200);
688c2ecf20Sopenharmony_ci		drm_set_preferred_mode(connector, 1024, 768);
698c2ecf20Sopenharmony_ci		return ret;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	drm_connector_update_edid_property(connector, edid);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	ret = drm_add_edid_modes(connector, edid);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	kfree(edid);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return ret;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs tfp410_con_helper_funcs = {
828c2ecf20Sopenharmony_ci	.get_modes	= tfp410_get_modes,
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic enum drm_connector_status
868c2ecf20Sopenharmony_citfp410_connector_detect(struct drm_connector *connector, bool force)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct tfp410 *dvi = drm_connector_to_tfp410(connector);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return drm_bridge_detect(dvi->next_bridge);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs tfp410_con_funcs = {
948c2ecf20Sopenharmony_ci	.detect			= tfp410_connector_detect,
958c2ecf20Sopenharmony_ci	.fill_modes		= drm_helper_probe_single_connector_modes,
968c2ecf20Sopenharmony_ci	.destroy		= drm_connector_cleanup,
978c2ecf20Sopenharmony_ci	.reset			= drm_atomic_helper_connector_reset,
988c2ecf20Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
998c2ecf20Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic void tfp410_hpd_work_func(struct work_struct *work)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct tfp410 *dvi;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	dvi = container_of(work, struct tfp410, hpd_work.work);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (dvi->bridge.dev)
1098c2ecf20Sopenharmony_ci		drm_helper_hpd_irq_event(dvi->bridge.dev);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void tfp410_hpd_callback(void *arg, enum drm_connector_status status)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct tfp410 *dvi = arg;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	mod_delayed_work(system_wq, &dvi->hpd_work,
1178c2ecf20Sopenharmony_ci			 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int tfp410_attach(struct drm_bridge *bridge,
1218c2ecf20Sopenharmony_ci			 enum drm_bridge_attach_flags flags)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
1248c2ecf20Sopenharmony_ci	int ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ret = drm_bridge_attach(bridge->encoder, dvi->next_bridge, bridge,
1278c2ecf20Sopenharmony_ci				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
1288c2ecf20Sopenharmony_ci	if (ret < 0)
1298c2ecf20Sopenharmony_ci		return ret;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
1328c2ecf20Sopenharmony_ci		return 0;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (!bridge->encoder) {
1358c2ecf20Sopenharmony_ci		dev_err(dvi->dev, "Missing encoder\n");
1368c2ecf20Sopenharmony_ci		return -ENODEV;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (dvi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)
1408c2ecf20Sopenharmony_ci		dvi->connector.polled = DRM_CONNECTOR_POLL_HPD;
1418c2ecf20Sopenharmony_ci	else
1428c2ecf20Sopenharmony_ci		dvi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
1458c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func);
1468c2ecf20Sopenharmony_ci		drm_bridge_hpd_enable(dvi->next_bridge, tfp410_hpd_callback,
1478c2ecf20Sopenharmony_ci				      dvi);
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	drm_connector_helper_add(&dvi->connector,
1518c2ecf20Sopenharmony_ci				 &tfp410_con_helper_funcs);
1528c2ecf20Sopenharmony_ci	ret = drm_connector_init_with_ddc(bridge->dev, &dvi->connector,
1538c2ecf20Sopenharmony_ci					  &tfp410_con_funcs,
1548c2ecf20Sopenharmony_ci					  dvi->next_bridge->type,
1558c2ecf20Sopenharmony_ci					  dvi->next_bridge->ddc);
1568c2ecf20Sopenharmony_ci	if (ret) {
1578c2ecf20Sopenharmony_ci		dev_err(dvi->dev, "drm_connector_init_with_ddc() failed: %d\n",
1588c2ecf20Sopenharmony_ci			ret);
1598c2ecf20Sopenharmony_ci		return ret;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	drm_display_info_set_bus_formats(&dvi->connector.display_info,
1638c2ecf20Sopenharmony_ci					 &dvi->bus_format, 1);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	drm_connector_attach_encoder(&dvi->connector, bridge->encoder);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic void tfp410_detach(struct drm_bridge *bridge)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (dvi->connector.dev && dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
1758c2ecf20Sopenharmony_ci		drm_bridge_hpd_disable(dvi->next_bridge);
1768c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&dvi->hpd_work);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void tfp410_enable(struct drm_bridge *bridge)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(dvi->powerdown, 0);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic void tfp410_disable(struct drm_bridge *bridge)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(dvi->powerdown, 1);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic enum drm_mode_status tfp410_mode_valid(struct drm_bridge *bridge,
1958c2ecf20Sopenharmony_ci					      const struct drm_display_info *info,
1968c2ecf20Sopenharmony_ci					      const struct drm_display_mode *mode)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	if (mode->clock < 25000)
1998c2ecf20Sopenharmony_ci		return MODE_CLOCK_LOW;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (mode->clock > 165000)
2028c2ecf20Sopenharmony_ci		return MODE_CLOCK_HIGH;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return MODE_OK;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs tfp410_bridge_funcs = {
2088c2ecf20Sopenharmony_ci	.attach		= tfp410_attach,
2098c2ecf20Sopenharmony_ci	.detach		= tfp410_detach,
2108c2ecf20Sopenharmony_ci	.enable		= tfp410_enable,
2118c2ecf20Sopenharmony_ci	.disable	= tfp410_disable,
2128c2ecf20Sopenharmony_ci	.mode_valid	= tfp410_mode_valid,
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic const struct drm_bridge_timings tfp410_default_timings = {
2168c2ecf20Sopenharmony_ci	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
2178c2ecf20Sopenharmony_ci			 | DRM_BUS_FLAG_DE_HIGH,
2188c2ecf20Sopenharmony_ci	.setup_time_ps = 1200,
2198c2ecf20Sopenharmony_ci	.hold_time_ps = 1300,
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int tfp410_parse_timings(struct tfp410 *dvi, bool i2c)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct drm_bridge_timings *timings = &dvi->timings;
2258c2ecf20Sopenharmony_ci	struct device_node *ep;
2268c2ecf20Sopenharmony_ci	u32 pclk_sample = 0;
2278c2ecf20Sopenharmony_ci	u32 bus_width = 24;
2288c2ecf20Sopenharmony_ci	u32 deskew = 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* Start with defaults. */
2318c2ecf20Sopenharmony_ci	*timings = tfp410_default_timings;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (i2c)
2348c2ecf20Sopenharmony_ci		/*
2358c2ecf20Sopenharmony_ci		 * In I2C mode timings are configured through the I2C interface.
2368c2ecf20Sopenharmony_ci		 * As the driver doesn't support I2C configuration yet, we just
2378c2ecf20Sopenharmony_ci		 * go with the defaults (BSEL=1, DSEL=1, DKEN=0, EDGE=1).
2388c2ecf20Sopenharmony_ci		 */
2398c2ecf20Sopenharmony_ci		return 0;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/*
2428c2ecf20Sopenharmony_ci	 * In non-I2C mode, timings are configured through the BSEL, DSEL, DKEN
2438c2ecf20Sopenharmony_ci	 * and EDGE pins. They are specified in DT through endpoint properties
2448c2ecf20Sopenharmony_ci	 * and vendor-specific properties.
2458c2ecf20Sopenharmony_ci	 */
2468c2ecf20Sopenharmony_ci	ep = of_graph_get_endpoint_by_regs(dvi->dev->of_node, 0, 0);
2478c2ecf20Sopenharmony_ci	if (!ep)
2488c2ecf20Sopenharmony_ci		return -EINVAL;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* Get the sampling edge from the endpoint. */
2518c2ecf20Sopenharmony_ci	of_property_read_u32(ep, "pclk-sample", &pclk_sample);
2528c2ecf20Sopenharmony_ci	of_property_read_u32(ep, "bus-width", &bus_width);
2538c2ecf20Sopenharmony_ci	of_node_put(ep);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	timings->input_bus_flags = DRM_BUS_FLAG_DE_HIGH;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	switch (pclk_sample) {
2588c2ecf20Sopenharmony_ci	case 0:
2598c2ecf20Sopenharmony_ci		timings->input_bus_flags |= DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE
2608c2ecf20Sopenharmony_ci					 |  DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE;
2618c2ecf20Sopenharmony_ci		break;
2628c2ecf20Sopenharmony_ci	case 1:
2638c2ecf20Sopenharmony_ci		timings->input_bus_flags |= DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
2648c2ecf20Sopenharmony_ci					 |  DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE;
2658c2ecf20Sopenharmony_ci		break;
2668c2ecf20Sopenharmony_ci	default:
2678c2ecf20Sopenharmony_ci		return -EINVAL;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	switch (bus_width) {
2718c2ecf20Sopenharmony_ci	case 12:
2728c2ecf20Sopenharmony_ci		dvi->bus_format = MEDIA_BUS_FMT_RGB888_2X12_LE;
2738c2ecf20Sopenharmony_ci		break;
2748c2ecf20Sopenharmony_ci	case 24:
2758c2ecf20Sopenharmony_ci		dvi->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci	default:
2788c2ecf20Sopenharmony_ci		return -EINVAL;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* Get the setup and hold time from vendor-specific properties. */
2828c2ecf20Sopenharmony_ci	of_property_read_u32(dvi->dev->of_node, "ti,deskew", &deskew);
2838c2ecf20Sopenharmony_ci	if (deskew > 7)
2848c2ecf20Sopenharmony_ci		return -EINVAL;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	timings->setup_time_ps = 1200 - 350 * ((s32)deskew - 4);
2878c2ecf20Sopenharmony_ci	timings->hold_time_ps = max(0, 1300 + 350 * ((s32)deskew - 4));
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return 0;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic int tfp410_init(struct device *dev, bool i2c)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct device_node *node;
2958c2ecf20Sopenharmony_ci	struct tfp410 *dvi;
2968c2ecf20Sopenharmony_ci	int ret;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (!dev->of_node) {
2998c2ecf20Sopenharmony_ci		dev_err(dev, "device-tree data is missing\n");
3008c2ecf20Sopenharmony_ci		return -ENXIO;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	dvi = devm_kzalloc(dev, sizeof(*dvi), GFP_KERNEL);
3048c2ecf20Sopenharmony_ci	if (!dvi)
3058c2ecf20Sopenharmony_ci		return -ENOMEM;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	dvi->dev = dev;
3088c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, dvi);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	dvi->bridge.funcs = &tfp410_bridge_funcs;
3118c2ecf20Sopenharmony_ci	dvi->bridge.of_node = dev->of_node;
3128c2ecf20Sopenharmony_ci	dvi->bridge.timings = &dvi->timings;
3138c2ecf20Sopenharmony_ci	dvi->bridge.type = DRM_MODE_CONNECTOR_DVID;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	ret = tfp410_parse_timings(dvi, i2c);
3168c2ecf20Sopenharmony_ci	if (ret)
3178c2ecf20Sopenharmony_ci		return ret;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* Get the next bridge, connected to port@1. */
3208c2ecf20Sopenharmony_ci	node = of_graph_get_remote_node(dev->of_node, 1, -1);
3218c2ecf20Sopenharmony_ci	if (!node)
3228c2ecf20Sopenharmony_ci		return -ENODEV;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	dvi->next_bridge = of_drm_find_bridge(node);
3258c2ecf20Sopenharmony_ci	of_node_put(node);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (!dvi->next_bridge)
3288c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* Get the powerdown GPIO. */
3318c2ecf20Sopenharmony_ci	dvi->powerdown = devm_gpiod_get_optional(dev, "powerdown",
3328c2ecf20Sopenharmony_ci						 GPIOD_OUT_HIGH);
3338c2ecf20Sopenharmony_ci	if (IS_ERR(dvi->powerdown)) {
3348c2ecf20Sopenharmony_ci		dev_err(dev, "failed to parse powerdown gpio\n");
3358c2ecf20Sopenharmony_ci		return PTR_ERR(dvi->powerdown);
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/*  Register the DRM bridge. */
3398c2ecf20Sopenharmony_ci	drm_bridge_add(&dvi->bridge);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int tfp410_fini(struct device *dev)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct tfp410 *dvi = dev_get_drvdata(dev);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	drm_bridge_remove(&dvi->bridge);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int tfp410_probe(struct platform_device *pdev)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	return tfp410_init(&pdev->dev, false);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int tfp410_remove(struct platform_device *pdev)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	return tfp410_fini(&pdev->dev);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic const struct of_device_id tfp410_match[] = {
3648c2ecf20Sopenharmony_ci	{ .compatible = "ti,tfp410" },
3658c2ecf20Sopenharmony_ci	{},
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tfp410_match);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic struct platform_driver tfp410_platform_driver = {
3708c2ecf20Sopenharmony_ci	.probe	= tfp410_probe,
3718c2ecf20Sopenharmony_ci	.remove	= tfp410_remove,
3728c2ecf20Sopenharmony_ci	.driver	= {
3738c2ecf20Sopenharmony_ci		.name		= "tfp410-bridge",
3748c2ecf20Sopenharmony_ci		.of_match_table	= tfp410_match,
3758c2ecf20Sopenharmony_ci	},
3768c2ecf20Sopenharmony_ci};
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C)
3798c2ecf20Sopenharmony_ci/* There is currently no i2c functionality. */
3808c2ecf20Sopenharmony_cistatic int tfp410_i2c_probe(struct i2c_client *client,
3818c2ecf20Sopenharmony_ci			    const struct i2c_device_id *id)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	int reg;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (!client->dev.of_node ||
3868c2ecf20Sopenharmony_ci	    of_property_read_u32(client->dev.of_node, "reg", &reg)) {
3878c2ecf20Sopenharmony_ci		dev_err(&client->dev,
3888c2ecf20Sopenharmony_ci			"Can't get i2c reg property from device-tree\n");
3898c2ecf20Sopenharmony_ci		return -ENXIO;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return tfp410_init(&client->dev, true);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int tfp410_i2c_remove(struct i2c_client *client)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	return tfp410_fini(&client->dev);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic const struct i2c_device_id tfp410_i2c_ids[] = {
4018c2ecf20Sopenharmony_ci	{ "tfp410", 0 },
4028c2ecf20Sopenharmony_ci	{ }
4038c2ecf20Sopenharmony_ci};
4048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic struct i2c_driver tfp410_i2c_driver = {
4078c2ecf20Sopenharmony_ci	.driver = {
4088c2ecf20Sopenharmony_ci		.name	= "tfp410",
4098c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(tfp410_match),
4108c2ecf20Sopenharmony_ci	},
4118c2ecf20Sopenharmony_ci	.id_table	= tfp410_i2c_ids,
4128c2ecf20Sopenharmony_ci	.probe		= tfp410_i2c_probe,
4138c2ecf20Sopenharmony_ci	.remove		= tfp410_i2c_remove,
4148c2ecf20Sopenharmony_ci};
4158c2ecf20Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_I2C) */
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic struct {
4188c2ecf20Sopenharmony_ci	uint i2c:1;
4198c2ecf20Sopenharmony_ci	uint platform:1;
4208c2ecf20Sopenharmony_ci}  tfp410_registered_driver;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int __init tfp410_module_init(void)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	int ret;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C)
4278c2ecf20Sopenharmony_ci	ret = i2c_add_driver(&tfp410_i2c_driver);
4288c2ecf20Sopenharmony_ci	if (ret)
4298c2ecf20Sopenharmony_ci		pr_err("%s: registering i2c driver failed: %d",
4308c2ecf20Sopenharmony_ci		       __func__, ret);
4318c2ecf20Sopenharmony_ci	else
4328c2ecf20Sopenharmony_ci		tfp410_registered_driver.i2c = 1;
4338c2ecf20Sopenharmony_ci#endif
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	ret = platform_driver_register(&tfp410_platform_driver);
4368c2ecf20Sopenharmony_ci	if (ret)
4378c2ecf20Sopenharmony_ci		pr_err("%s: registering platform driver failed: %d",
4388c2ecf20Sopenharmony_ci		       __func__, ret);
4398c2ecf20Sopenharmony_ci	else
4408c2ecf20Sopenharmony_ci		tfp410_registered_driver.platform = 1;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (tfp410_registered_driver.i2c ||
4438c2ecf20Sopenharmony_ci	    tfp410_registered_driver.platform)
4448c2ecf20Sopenharmony_ci		return 0;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	return ret;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_cimodule_init(tfp410_module_init);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic void __exit tfp410_module_exit(void)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C)
4538c2ecf20Sopenharmony_ci	if (tfp410_registered_driver.i2c)
4548c2ecf20Sopenharmony_ci		i2c_del_driver(&tfp410_i2c_driver);
4558c2ecf20Sopenharmony_ci#endif
4568c2ecf20Sopenharmony_ci	if (tfp410_registered_driver.platform)
4578c2ecf20Sopenharmony_ci		platform_driver_unregister(&tfp410_platform_driver);
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_cimodule_exit(tfp410_module_exit);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
4628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI TFP410 DVI bridge driver");
4638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
464