18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * NXP PTN3460 DP/LVDS bridge driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Google, Inc.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
108c2ecf20Sopenharmony_ci#include <linux/i2c.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_edid.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_of.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_panel.h>
198c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
208c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define PTN3460_EDID_ADDR			0x0
238c2ecf20Sopenharmony_ci#define PTN3460_EDID_EMULATION_ADDR		0x84
248c2ecf20Sopenharmony_ci#define PTN3460_EDID_ENABLE_EMULATION		0
258c2ecf20Sopenharmony_ci#define PTN3460_EDID_EMULATION_SELECTION	1
268c2ecf20Sopenharmony_ci#define PTN3460_EDID_SRAM_LOAD_ADDR		0x85
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct ptn3460_bridge {
298c2ecf20Sopenharmony_ci	struct drm_connector connector;
308c2ecf20Sopenharmony_ci	struct i2c_client *client;
318c2ecf20Sopenharmony_ci	struct drm_bridge bridge;
328c2ecf20Sopenharmony_ci	struct drm_bridge *panel_bridge;
338c2ecf20Sopenharmony_ci	struct gpio_desc *gpio_pd_n;
348c2ecf20Sopenharmony_ci	struct gpio_desc *gpio_rst_n;
358c2ecf20Sopenharmony_ci	u32 edid_emulation;
368c2ecf20Sopenharmony_ci	bool enabled;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic inline struct ptn3460_bridge *
408c2ecf20Sopenharmony_ci		bridge_to_ptn3460(struct drm_bridge *bridge)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return container_of(bridge, struct ptn3460_bridge, bridge);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline struct ptn3460_bridge *
468c2ecf20Sopenharmony_ci		connector_to_ptn3460(struct drm_connector *connector)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return container_of(connector, struct ptn3460_bridge, connector);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
528c2ecf20Sopenharmony_ci		u8 *buf, int len)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int ret;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	ret = i2c_master_send(ptn_bridge->client, &addr, 1);
578c2ecf20Sopenharmony_ci	if (ret < 0) {
588c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
598c2ecf20Sopenharmony_ci		return ret;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	ret = i2c_master_recv(ptn_bridge->client, buf, len);
638c2ecf20Sopenharmony_ci	if (ret < 0) {
648c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
658c2ecf20Sopenharmony_ci		return ret;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
728c2ecf20Sopenharmony_ci		char val)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	int ret;
758c2ecf20Sopenharmony_ci	char buf[2];
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	buf[0] = addr;
788c2ecf20Sopenharmony_ci	buf[1] = val;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
818c2ecf20Sopenharmony_ci	if (ret < 0) {
828c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
838c2ecf20Sopenharmony_ci		return ret;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int ret;
928c2ecf20Sopenharmony_ci	char val;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
958c2ecf20Sopenharmony_ci	ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
968c2ecf20Sopenharmony_ci			ptn_bridge->edid_emulation);
978c2ecf20Sopenharmony_ci	if (ret) {
988c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to transfer EDID to sram, ret=%d\n", ret);
998c2ecf20Sopenharmony_ci		return ret;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Enable EDID emulation and select the desired EDID */
1038c2ecf20Sopenharmony_ci	val = 1 << PTN3460_EDID_ENABLE_EMULATION |
1048c2ecf20Sopenharmony_ci		ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
1078c2ecf20Sopenharmony_ci	if (ret) {
1088c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to write EDID value, ret=%d\n", ret);
1098c2ecf20Sopenharmony_ci		return ret;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void ptn3460_pre_enable(struct drm_bridge *bridge)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
1188c2ecf20Sopenharmony_ci	int ret;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (ptn_bridge->enabled)
1218c2ecf20Sopenharmony_ci		return;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	gpiod_set_value(ptn_bridge->gpio_pd_n, 1);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	gpiod_set_value(ptn_bridge->gpio_rst_n, 0);
1268c2ecf20Sopenharmony_ci	usleep_range(10, 20);
1278c2ecf20Sopenharmony_ci	gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/*
1308c2ecf20Sopenharmony_ci	 * There's a bug in the PTN chip where it falsely asserts hotplug before
1318c2ecf20Sopenharmony_ci	 * it is fully functional. We're forced to wait for the maximum start up
1328c2ecf20Sopenharmony_ci	 * time specified in the chip's datasheet to make sure we're really up.
1338c2ecf20Sopenharmony_ci	 */
1348c2ecf20Sopenharmony_ci	msleep(90);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ret = ptn3460_select_edid(ptn_bridge);
1378c2ecf20Sopenharmony_ci	if (ret)
1388c2ecf20Sopenharmony_ci		DRM_ERROR("Select EDID failed ret=%d\n", ret);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	ptn_bridge->enabled = true;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void ptn3460_disable(struct drm_bridge *bridge)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!ptn_bridge->enabled)
1488c2ecf20Sopenharmony_ci		return;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	ptn_bridge->enabled = false;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
1538c2ecf20Sopenharmony_ci	gpiod_set_value(ptn_bridge->gpio_pd_n, 0);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic struct edid *ptn3460_get_edid(struct drm_bridge *bridge,
1588c2ecf20Sopenharmony_ci				     struct drm_connector *connector)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
1618c2ecf20Sopenharmony_ci	bool power_off;
1628c2ecf20Sopenharmony_ci	u8 *edid;
1638c2ecf20Sopenharmony_ci	int ret;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	power_off = !ptn_bridge->enabled;
1668c2ecf20Sopenharmony_ci	ptn3460_pre_enable(&ptn_bridge->bridge);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
1698c2ecf20Sopenharmony_ci	if (!edid) {
1708c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to allocate EDID\n");
1718c2ecf20Sopenharmony_ci		goto out;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
1758c2ecf20Sopenharmony_ci				 EDID_LENGTH);
1768c2ecf20Sopenharmony_ci	if (ret) {
1778c2ecf20Sopenharmony_ci		kfree(edid);
1788c2ecf20Sopenharmony_ci		edid = NULL;
1798c2ecf20Sopenharmony_ci		goto out;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciout:
1838c2ecf20Sopenharmony_ci	if (power_off)
1848c2ecf20Sopenharmony_ci		ptn3460_disable(&ptn_bridge->bridge);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return (struct edid *)edid;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int ptn3460_connector_get_modes(struct drm_connector *connector)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector);
1928c2ecf20Sopenharmony_ci	struct edid *edid;
1938c2ecf20Sopenharmony_ci	int num_modes;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	edid = ptn3460_get_edid(&ptn_bridge->bridge, connector);
1968c2ecf20Sopenharmony_ci	drm_connector_update_edid_property(connector, edid);
1978c2ecf20Sopenharmony_ci	num_modes = drm_add_edid_modes(connector, edid);
1988c2ecf20Sopenharmony_ci	kfree(edid);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return num_modes;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
2048c2ecf20Sopenharmony_ci	.get_modes = ptn3460_connector_get_modes,
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs ptn3460_connector_funcs = {
2088c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
2098c2ecf20Sopenharmony_ci	.destroy = drm_connector_cleanup,
2108c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
2118c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
2128c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int ptn3460_bridge_attach(struct drm_bridge *bridge,
2168c2ecf20Sopenharmony_ci				 enum drm_bridge_attach_flags flags)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
2198c2ecf20Sopenharmony_ci	int ret;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Let this driver create connector if requested */
2228c2ecf20Sopenharmony_ci	ret = drm_bridge_attach(bridge->encoder, ptn_bridge->panel_bridge,
2238c2ecf20Sopenharmony_ci				bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
2248c2ecf20Sopenharmony_ci	if (ret < 0)
2258c2ecf20Sopenharmony_ci		return ret;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
2288c2ecf20Sopenharmony_ci		return 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (!bridge->encoder) {
2318c2ecf20Sopenharmony_ci		DRM_ERROR("Parent encoder object not found");
2328c2ecf20Sopenharmony_ci		return -ENODEV;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ptn_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
2368c2ecf20Sopenharmony_ci	ret = drm_connector_init(bridge->dev, &ptn_bridge->connector,
2378c2ecf20Sopenharmony_ci			&ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
2388c2ecf20Sopenharmony_ci	if (ret) {
2398c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to initialize connector with drm\n");
2408c2ecf20Sopenharmony_ci		return ret;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci	drm_connector_helper_add(&ptn_bridge->connector,
2438c2ecf20Sopenharmony_ci					&ptn3460_connector_helper_funcs);
2448c2ecf20Sopenharmony_ci	drm_connector_register(&ptn_bridge->connector);
2458c2ecf20Sopenharmony_ci	drm_connector_attach_encoder(&ptn_bridge->connector,
2468c2ecf20Sopenharmony_ci							bridge->encoder);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	drm_helper_hpd_irq_event(ptn_bridge->connector.dev);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return ret;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs ptn3460_bridge_funcs = {
2548c2ecf20Sopenharmony_ci	.pre_enable = ptn3460_pre_enable,
2558c2ecf20Sopenharmony_ci	.disable = ptn3460_disable,
2568c2ecf20Sopenharmony_ci	.attach = ptn3460_bridge_attach,
2578c2ecf20Sopenharmony_ci	.get_edid = ptn3460_get_edid,
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int ptn3460_probe(struct i2c_client *client,
2618c2ecf20Sopenharmony_ci				const struct i2c_device_id *id)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
2648c2ecf20Sopenharmony_ci	struct ptn3460_bridge *ptn_bridge;
2658c2ecf20Sopenharmony_ci	struct drm_bridge *panel_bridge;
2668c2ecf20Sopenharmony_ci	struct drm_panel *panel;
2678c2ecf20Sopenharmony_ci	int ret;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL);
2708c2ecf20Sopenharmony_ci	if (!ptn_bridge) {
2718c2ecf20Sopenharmony_ci		return -ENOMEM;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL);
2758c2ecf20Sopenharmony_ci	if (ret)
2768c2ecf20Sopenharmony_ci		return ret;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	panel_bridge = devm_drm_panel_bridge_add(dev, panel);
2798c2ecf20Sopenharmony_ci	if (IS_ERR(panel_bridge))
2808c2ecf20Sopenharmony_ci		return PTR_ERR(panel_bridge);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	ptn_bridge->panel_bridge = panel_bridge;
2838c2ecf20Sopenharmony_ci	ptn_bridge->client = client;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	ptn_bridge->gpio_pd_n = devm_gpiod_get(&client->dev, "powerdown",
2868c2ecf20Sopenharmony_ci					       GPIOD_OUT_HIGH);
2878c2ecf20Sopenharmony_ci	if (IS_ERR(ptn_bridge->gpio_pd_n)) {
2888c2ecf20Sopenharmony_ci		ret = PTR_ERR(ptn_bridge->gpio_pd_n);
2898c2ecf20Sopenharmony_ci		dev_err(dev, "cannot get gpio_pd_n %d\n", ret);
2908c2ecf20Sopenharmony_ci		return ret;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/*
2948c2ecf20Sopenharmony_ci	 * Request the reset pin low to avoid the bridge being
2958c2ecf20Sopenharmony_ci	 * initialized prematurely
2968c2ecf20Sopenharmony_ci	 */
2978c2ecf20Sopenharmony_ci	ptn_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset",
2988c2ecf20Sopenharmony_ci						GPIOD_OUT_LOW);
2998c2ecf20Sopenharmony_ci	if (IS_ERR(ptn_bridge->gpio_rst_n)) {
3008c2ecf20Sopenharmony_ci		ret = PTR_ERR(ptn_bridge->gpio_rst_n);
3018c2ecf20Sopenharmony_ci		DRM_ERROR("cannot get gpio_rst_n %d\n", ret);
3028c2ecf20Sopenharmony_ci		return ret;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	ret = of_property_read_u32(dev->of_node, "edid-emulation",
3068c2ecf20Sopenharmony_ci			&ptn_bridge->edid_emulation);
3078c2ecf20Sopenharmony_ci	if (ret) {
3088c2ecf20Sopenharmony_ci		dev_err(dev, "Can't read EDID emulation value\n");
3098c2ecf20Sopenharmony_ci		return ret;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs;
3138c2ecf20Sopenharmony_ci	ptn_bridge->bridge.ops = DRM_BRIDGE_OP_EDID;
3148c2ecf20Sopenharmony_ci	ptn_bridge->bridge.type = DRM_MODE_CONNECTOR_LVDS;
3158c2ecf20Sopenharmony_ci	ptn_bridge->bridge.of_node = dev->of_node;
3168c2ecf20Sopenharmony_ci	drm_bridge_add(&ptn_bridge->bridge);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, ptn_bridge);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return 0;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic int ptn3460_remove(struct i2c_client *client)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct ptn3460_bridge *ptn_bridge = i2c_get_clientdata(client);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	drm_bridge_remove(&ptn_bridge->bridge);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic const struct i2c_device_id ptn3460_i2c_table[] = {
3338c2ecf20Sopenharmony_ci	{"ptn3460", 0},
3348c2ecf20Sopenharmony_ci	{},
3358c2ecf20Sopenharmony_ci};
3368c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic const struct of_device_id ptn3460_match[] = {
3398c2ecf20Sopenharmony_ci	{ .compatible = "nxp,ptn3460" },
3408c2ecf20Sopenharmony_ci	{},
3418c2ecf20Sopenharmony_ci};
3428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ptn3460_match);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic struct i2c_driver ptn3460_driver = {
3458c2ecf20Sopenharmony_ci	.id_table	= ptn3460_i2c_table,
3468c2ecf20Sopenharmony_ci	.probe		= ptn3460_probe,
3478c2ecf20Sopenharmony_ci	.remove		= ptn3460_remove,
3488c2ecf20Sopenharmony_ci	.driver		= {
3498c2ecf20Sopenharmony_ci		.name	= "nxp,ptn3460",
3508c2ecf20Sopenharmony_ci		.of_match_table = ptn3460_match,
3518c2ecf20Sopenharmony_ci	},
3528c2ecf20Sopenharmony_ci};
3538c2ecf20Sopenharmony_cimodule_i2c_driver(ptn3460_driver);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Paul <seanpaul@chromium.org>");
3568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NXP ptn3460 eDP-LVDS converter driver");
3578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
358