18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for MegaChips STDP4028 with GE B850v3 firmware (LVDS-DP)
48c2ecf20Sopenharmony_ci * Driver for MegaChips STDP2690 with GE B850v3 firmware (DP-DP++)
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci * Copyright (c) 2017, Collabora Ltd.
78c2ecf20Sopenharmony_ci * Copyright (c) 2017, General Electric Company
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci * This driver creates a drm_bridge and a drm_connector for the LVDS to DP++
118c2ecf20Sopenharmony_ci * display bridge of the GE B850v3. There are two physical bridges on the video
128c2ecf20Sopenharmony_ci * signal pipeline: a STDP4028(LVDS to DP) and a STDP2690(DP to DP++). The
138c2ecf20Sopenharmony_ci * physical bridges are automatically configured by the input video signal, and
148c2ecf20Sopenharmony_ci * the driver has no access to the video processing pipeline. The driver is
158c2ecf20Sopenharmony_ci * only needed to read EDID from the STDP2690 and to handle HPD events from the
168c2ecf20Sopenharmony_ci * STDP4028. The driver communicates with both bridges over i2c. The video
178c2ecf20Sopenharmony_ci * signal pipeline is as follows:
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *   Host -> LVDS|--(STDP4028)--|DP -> DP|--(STDP2690)--|DP++ -> Video output
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/i2c.h>
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <linux/of.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
278c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
288c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h>
298c2ecf20Sopenharmony_ci#include <drm/drm_edid.h>
308c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
318c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define EDID_EXT_BLOCK_CNT 0x7E
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define STDP4028_IRQ_OUT_CONF_REG 0x02
368c2ecf20Sopenharmony_ci#define STDP4028_DPTX_IRQ_EN_REG 0x3C
378c2ecf20Sopenharmony_ci#define STDP4028_DPTX_IRQ_STS_REG 0x3D
388c2ecf20Sopenharmony_ci#define STDP4028_DPTX_STS_REG 0x3E
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define STDP4028_DPTX_DP_IRQ_EN 0x1000
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define STDP4028_DPTX_HOTPLUG_IRQ_EN 0x0400
438c2ecf20Sopenharmony_ci#define STDP4028_DPTX_LINK_CH_IRQ_EN 0x2000
448c2ecf20Sopenharmony_ci#define STDP4028_DPTX_IRQ_CONFIG \
458c2ecf20Sopenharmony_ci		(STDP4028_DPTX_LINK_CH_IRQ_EN | STDP4028_DPTX_HOTPLUG_IRQ_EN)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define STDP4028_DPTX_HOTPLUG_STS 0x0200
488c2ecf20Sopenharmony_ci#define STDP4028_DPTX_LINK_STS 0x1000
498c2ecf20Sopenharmony_ci#define STDP4028_CON_STATE_CONNECTED \
508c2ecf20Sopenharmony_ci		(STDP4028_DPTX_HOTPLUG_STS | STDP4028_DPTX_LINK_STS)
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define STDP4028_DPTX_HOTPLUG_CH_STS 0x0400
538c2ecf20Sopenharmony_ci#define STDP4028_DPTX_LINK_CH_STS 0x2000
548c2ecf20Sopenharmony_ci#define STDP4028_DPTX_IRQ_CLEAR \
558c2ecf20Sopenharmony_ci		(STDP4028_DPTX_LINK_CH_STS | STDP4028_DPTX_HOTPLUG_CH_STS)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ge_b850v3_lvds_dev_mutex);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct ge_b850v3_lvds {
608c2ecf20Sopenharmony_ci	struct drm_connector connector;
618c2ecf20Sopenharmony_ci	struct drm_bridge bridge;
628c2ecf20Sopenharmony_ci	struct i2c_client *stdp4028_i2c;
638c2ecf20Sopenharmony_ci	struct i2c_client *stdp2690_i2c;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct ge_b850v3_lvds *ge_b850v3_lvds_ptr;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic u8 *stdp2690_get_edid(struct i2c_client *client)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
718c2ecf20Sopenharmony_ci	unsigned char start = 0x00;
728c2ecf20Sopenharmony_ci	unsigned int total_size;
738c2ecf20Sopenharmony_ci	u8 *block = kmalloc(EDID_LENGTH, GFP_KERNEL);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	struct i2c_msg msgs[] = {
768c2ecf20Sopenharmony_ci		{
778c2ecf20Sopenharmony_ci			.addr	= client->addr,
788c2ecf20Sopenharmony_ci			.flags	= 0,
798c2ecf20Sopenharmony_ci			.len	= 1,
808c2ecf20Sopenharmony_ci			.buf	= &start,
818c2ecf20Sopenharmony_ci		}, {
828c2ecf20Sopenharmony_ci			.addr	= client->addr,
838c2ecf20Sopenharmony_ci			.flags	= I2C_M_RD,
848c2ecf20Sopenharmony_ci			.len	= EDID_LENGTH,
858c2ecf20Sopenharmony_ci			.buf	= block,
868c2ecf20Sopenharmony_ci		}
878c2ecf20Sopenharmony_ci	};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!block)
908c2ecf20Sopenharmony_ci		return NULL;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (i2c_transfer(adapter, msgs, 2) != 2) {
938c2ecf20Sopenharmony_ci		DRM_ERROR("Unable to read EDID.\n");
948c2ecf20Sopenharmony_ci		goto err;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (!drm_edid_block_valid(block, 0, false, NULL)) {
988c2ecf20Sopenharmony_ci		DRM_ERROR("Invalid EDID data\n");
998c2ecf20Sopenharmony_ci		goto err;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	total_size = (block[EDID_EXT_BLOCK_CNT] + 1) * EDID_LENGTH;
1038c2ecf20Sopenharmony_ci	if (total_size > EDID_LENGTH) {
1048c2ecf20Sopenharmony_ci		kfree(block);
1058c2ecf20Sopenharmony_ci		block = kmalloc(total_size, GFP_KERNEL);
1068c2ecf20Sopenharmony_ci		if (!block)
1078c2ecf20Sopenharmony_ci			return NULL;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		/* Yes, read the entire buffer, and do not skip the first
1108c2ecf20Sopenharmony_ci		 * EDID_LENGTH bytes.
1118c2ecf20Sopenharmony_ci		 */
1128c2ecf20Sopenharmony_ci		start = 0x00;
1138c2ecf20Sopenharmony_ci		msgs[1].len = total_size;
1148c2ecf20Sopenharmony_ci		msgs[1].buf = block;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		if (i2c_transfer(adapter, msgs, 2) != 2) {
1178c2ecf20Sopenharmony_ci			DRM_ERROR("Unable to read EDID extension blocks.\n");
1188c2ecf20Sopenharmony_ci			goto err;
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci		if (!drm_edid_block_valid(block, 1, false, NULL)) {
1218c2ecf20Sopenharmony_ci			DRM_ERROR("Invalid EDID data\n");
1228c2ecf20Sopenharmony_ci			goto err;
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return block;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cierr:
1298c2ecf20Sopenharmony_ci	kfree(block);
1308c2ecf20Sopenharmony_ci	return NULL;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic struct edid *ge_b850v3_lvds_get_edid(struct drm_bridge *bridge,
1348c2ecf20Sopenharmony_ci					    struct drm_connector *connector)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct i2c_client *client;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	client = ge_b850v3_lvds_ptr->stdp2690_i2c;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return (struct edid *)stdp2690_get_edid(client);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic int ge_b850v3_lvds_get_modes(struct drm_connector *connector)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct edid *edid;
1468c2ecf20Sopenharmony_ci	int num_modes;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	edid = ge_b850v3_lvds_get_edid(&ge_b850v3_lvds_ptr->bridge, connector);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	drm_connector_update_edid_property(connector, edid);
1518c2ecf20Sopenharmony_ci	num_modes = drm_add_edid_modes(connector, edid);
1528c2ecf20Sopenharmony_ci	kfree(edid);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return num_modes;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic enum drm_mode_status ge_b850v3_lvds_mode_valid(
1588c2ecf20Sopenharmony_ci		struct drm_connector *connector, struct drm_display_mode *mode)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return MODE_OK;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic const struct
1648c2ecf20Sopenharmony_cidrm_connector_helper_funcs ge_b850v3_lvds_connector_helper_funcs = {
1658c2ecf20Sopenharmony_ci	.get_modes = ge_b850v3_lvds_get_modes,
1668c2ecf20Sopenharmony_ci	.mode_valid = ge_b850v3_lvds_mode_valid,
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic enum drm_connector_status ge_b850v3_lvds_bridge_detect(struct drm_bridge *bridge)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct i2c_client *stdp4028_i2c =
1728c2ecf20Sopenharmony_ci			ge_b850v3_lvds_ptr->stdp4028_i2c;
1738c2ecf20Sopenharmony_ci	s32 link_state;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	link_state = i2c_smbus_read_word_data(stdp4028_i2c,
1768c2ecf20Sopenharmony_ci					      STDP4028_DPTX_STS_REG);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (link_state == STDP4028_CON_STATE_CONNECTED)
1798c2ecf20Sopenharmony_ci		return connector_status_connected;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (link_state == 0)
1828c2ecf20Sopenharmony_ci		return connector_status_disconnected;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return connector_status_unknown;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic enum drm_connector_status ge_b850v3_lvds_detect(struct drm_connector *connector,
1888c2ecf20Sopenharmony_ci						       bool force)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	return ge_b850v3_lvds_bridge_detect(&ge_b850v3_lvds_ptr->bridge);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = {
1948c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
1958c2ecf20Sopenharmony_ci	.detect = ge_b850v3_lvds_detect,
1968c2ecf20Sopenharmony_ci	.destroy = drm_connector_cleanup,
1978c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
1988c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
1998c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int ge_b850v3_lvds_create_connector(struct drm_bridge *bridge)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector;
2058c2ecf20Sopenharmony_ci	int ret;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (!bridge->encoder) {
2088c2ecf20Sopenharmony_ci		DRM_ERROR("Parent encoder object not found");
2098c2ecf20Sopenharmony_ci		return -ENODEV;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	connector->polled = DRM_CONNECTOR_POLL_HPD;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	drm_connector_helper_add(connector,
2158c2ecf20Sopenharmony_ci				 &ge_b850v3_lvds_connector_helper_funcs);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	ret = drm_connector_init(bridge->dev, connector,
2188c2ecf20Sopenharmony_ci				 &ge_b850v3_lvds_connector_funcs,
2198c2ecf20Sopenharmony_ci				 DRM_MODE_CONNECTOR_DisplayPort);
2208c2ecf20Sopenharmony_ci	if (ret) {
2218c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to initialize connector with drm\n");
2228c2ecf20Sopenharmony_ci		return ret;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return drm_connector_attach_encoder(connector, bridge->encoder);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct i2c_client *stdp4028_i2c
2318c2ecf20Sopenharmony_ci			= ge_b850v3_lvds_ptr->stdp4028_i2c;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	i2c_smbus_write_word_data(stdp4028_i2c,
2348c2ecf20Sopenharmony_ci				  STDP4028_DPTX_IRQ_STS_REG,
2358c2ecf20Sopenharmony_ci				  STDP4028_DPTX_IRQ_CLEAR);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (ge_b850v3_lvds_ptr->bridge.dev)
2388c2ecf20Sopenharmony_ci		drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->bridge.dev);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
2448c2ecf20Sopenharmony_ci				 enum drm_bridge_attach_flags flags)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct i2c_client *stdp4028_i2c
2478c2ecf20Sopenharmony_ci			= ge_b850v3_lvds_ptr->stdp4028_i2c;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* Configures the bridge to re-enable interrupts after each ack. */
2508c2ecf20Sopenharmony_ci	i2c_smbus_write_word_data(stdp4028_i2c,
2518c2ecf20Sopenharmony_ci				  STDP4028_IRQ_OUT_CONF_REG,
2528c2ecf20Sopenharmony_ci				  STDP4028_DPTX_DP_IRQ_EN);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* Enable interrupts */
2558c2ecf20Sopenharmony_ci	i2c_smbus_write_word_data(stdp4028_i2c,
2568c2ecf20Sopenharmony_ci				  STDP4028_DPTX_IRQ_EN_REG,
2578c2ecf20Sopenharmony_ci				  STDP4028_DPTX_IRQ_CONFIG);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
2608c2ecf20Sopenharmony_ci		return 0;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return ge_b850v3_lvds_create_connector(bridge);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs ge_b850v3_lvds_funcs = {
2668c2ecf20Sopenharmony_ci	.attach = ge_b850v3_lvds_attach,
2678c2ecf20Sopenharmony_ci	.detect = ge_b850v3_lvds_bridge_detect,
2688c2ecf20Sopenharmony_ci	.get_edid = ge_b850v3_lvds_get_edid,
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int ge_b850v3_lvds_init(struct device *dev)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	mutex_lock(&ge_b850v3_lvds_dev_mutex);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (ge_b850v3_lvds_ptr)
2768c2ecf20Sopenharmony_ci		goto success;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	ge_b850v3_lvds_ptr = devm_kzalloc(dev,
2798c2ecf20Sopenharmony_ci					  sizeof(*ge_b850v3_lvds_ptr),
2808c2ecf20Sopenharmony_ci					  GFP_KERNEL);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (!ge_b850v3_lvds_ptr) {
2838c2ecf20Sopenharmony_ci		mutex_unlock(&ge_b850v3_lvds_dev_mutex);
2848c2ecf20Sopenharmony_ci		return -ENOMEM;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cisuccess:
2888c2ecf20Sopenharmony_ci	mutex_unlock(&ge_b850v3_lvds_dev_mutex);
2898c2ecf20Sopenharmony_ci	return 0;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic void ge_b850v3_lvds_remove(void)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	mutex_lock(&ge_b850v3_lvds_dev_mutex);
2958c2ecf20Sopenharmony_ci	/*
2968c2ecf20Sopenharmony_ci	 * This check is to avoid both the drivers
2978c2ecf20Sopenharmony_ci	 * removing the bridge in their remove() function
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	if (!ge_b850v3_lvds_ptr ||
3008c2ecf20Sopenharmony_ci	    !ge_b850v3_lvds_ptr->stdp2690_i2c ||
3018c2ecf20Sopenharmony_ci		!ge_b850v3_lvds_ptr->stdp4028_i2c)
3028c2ecf20Sopenharmony_ci		goto out;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	ge_b850v3_lvds_ptr = NULL;
3078c2ecf20Sopenharmony_ciout:
3088c2ecf20Sopenharmony_ci	mutex_unlock(&ge_b850v3_lvds_dev_mutex);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int ge_b850v3_register(void)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c;
3148c2ecf20Sopenharmony_ci	struct device *dev = &stdp4028_i2c->dev;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* drm bridge initialization */
3178c2ecf20Sopenharmony_ci	ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs;
3188c2ecf20Sopenharmony_ci	ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT |
3198c2ecf20Sopenharmony_ci					 DRM_BRIDGE_OP_EDID;
3208c2ecf20Sopenharmony_ci	ge_b850v3_lvds_ptr->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
3218c2ecf20Sopenharmony_ci	ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node;
3228c2ecf20Sopenharmony_ci	drm_bridge_add(&ge_b850v3_lvds_ptr->bridge);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* Clear pending interrupts since power up. */
3258c2ecf20Sopenharmony_ci	i2c_smbus_write_word_data(stdp4028_i2c,
3268c2ecf20Sopenharmony_ci				  STDP4028_DPTX_IRQ_STS_REG,
3278c2ecf20Sopenharmony_ci				  STDP4028_DPTX_IRQ_CLEAR);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (!stdp4028_i2c->irq)
3308c2ecf20Sopenharmony_ci		return 0;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return devm_request_threaded_irq(&stdp4028_i2c->dev,
3338c2ecf20Sopenharmony_ci			stdp4028_i2c->irq, NULL,
3348c2ecf20Sopenharmony_ci			ge_b850v3_lvds_irq_handler,
3358c2ecf20Sopenharmony_ci			IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
3368c2ecf20Sopenharmony_ci			"ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr);
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c,
3408c2ecf20Sopenharmony_ci				       const struct i2c_device_id *id)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct device *dev = &stdp4028_i2c->dev;
3438c2ecf20Sopenharmony_ci	int ret;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	ret = ge_b850v3_lvds_init(dev);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (ret)
3488c2ecf20Sopenharmony_ci		return ret;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c;
3518c2ecf20Sopenharmony_ci	i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* Only register after both bridges are probed */
3548c2ecf20Sopenharmony_ci	if (!ge_b850v3_lvds_ptr->stdp2690_i2c)
3558c2ecf20Sopenharmony_ci		return 0;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return ge_b850v3_register();
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int stdp4028_ge_b850v3_fw_remove(struct i2c_client *stdp4028_i2c)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	ge_b850v3_lvds_remove();
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return 0;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic const struct i2c_device_id stdp4028_ge_b850v3_fw_i2c_table[] = {
3688c2ecf20Sopenharmony_ci	{"stdp4028_ge_fw", 0},
3698c2ecf20Sopenharmony_ci	{},
3708c2ecf20Sopenharmony_ci};
3718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, stdp4028_ge_b850v3_fw_i2c_table);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic const struct of_device_id stdp4028_ge_b850v3_fw_match[] = {
3748c2ecf20Sopenharmony_ci	{ .compatible = "megachips,stdp4028-ge-b850v3-fw" },
3758c2ecf20Sopenharmony_ci	{},
3768c2ecf20Sopenharmony_ci};
3778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stdp4028_ge_b850v3_fw_match);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic struct i2c_driver stdp4028_ge_b850v3_fw_driver = {
3808c2ecf20Sopenharmony_ci	.id_table	= stdp4028_ge_b850v3_fw_i2c_table,
3818c2ecf20Sopenharmony_ci	.probe		= stdp4028_ge_b850v3_fw_probe,
3828c2ecf20Sopenharmony_ci	.remove		= stdp4028_ge_b850v3_fw_remove,
3838c2ecf20Sopenharmony_ci	.driver		= {
3848c2ecf20Sopenharmony_ci		.name		= "stdp4028-ge-b850v3-fw",
3858c2ecf20Sopenharmony_ci		.of_match_table = stdp4028_ge_b850v3_fw_match,
3868c2ecf20Sopenharmony_ci	},
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c,
3908c2ecf20Sopenharmony_ci				       const struct i2c_device_id *id)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct device *dev = &stdp2690_i2c->dev;
3938c2ecf20Sopenharmony_ci	int ret;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ret = ge_b850v3_lvds_init(dev);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (ret)
3988c2ecf20Sopenharmony_ci		return ret;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	ge_b850v3_lvds_ptr->stdp2690_i2c = stdp2690_i2c;
4018c2ecf20Sopenharmony_ci	i2c_set_clientdata(stdp2690_i2c, ge_b850v3_lvds_ptr);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* Only register after both bridges are probed */
4048c2ecf20Sopenharmony_ci	if (!ge_b850v3_lvds_ptr->stdp4028_i2c)
4058c2ecf20Sopenharmony_ci		return 0;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return ge_b850v3_register();
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int stdp2690_ge_b850v3_fw_remove(struct i2c_client *stdp2690_i2c)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	ge_b850v3_lvds_remove();
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic const struct i2c_device_id stdp2690_ge_b850v3_fw_i2c_table[] = {
4188c2ecf20Sopenharmony_ci	{"stdp2690_ge_fw", 0},
4198c2ecf20Sopenharmony_ci	{},
4208c2ecf20Sopenharmony_ci};
4218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, stdp2690_ge_b850v3_fw_i2c_table);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic const struct of_device_id stdp2690_ge_b850v3_fw_match[] = {
4248c2ecf20Sopenharmony_ci	{ .compatible = "megachips,stdp2690-ge-b850v3-fw" },
4258c2ecf20Sopenharmony_ci	{},
4268c2ecf20Sopenharmony_ci};
4278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stdp2690_ge_b850v3_fw_match);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic struct i2c_driver stdp2690_ge_b850v3_fw_driver = {
4308c2ecf20Sopenharmony_ci	.id_table	= stdp2690_ge_b850v3_fw_i2c_table,
4318c2ecf20Sopenharmony_ci	.probe		= stdp2690_ge_b850v3_fw_probe,
4328c2ecf20Sopenharmony_ci	.remove		= stdp2690_ge_b850v3_fw_remove,
4338c2ecf20Sopenharmony_ci	.driver		= {
4348c2ecf20Sopenharmony_ci		.name		= "stdp2690-ge-b850v3-fw",
4358c2ecf20Sopenharmony_ci		.of_match_table = stdp2690_ge_b850v3_fw_match,
4368c2ecf20Sopenharmony_ci	},
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int __init stdpxxxx_ge_b850v3_init(void)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	int ret;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	ret = i2c_add_driver(&stdp4028_ge_b850v3_fw_driver);
4448c2ecf20Sopenharmony_ci	if (ret)
4458c2ecf20Sopenharmony_ci		return ret;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	ret = i2c_add_driver(&stdp2690_ge_b850v3_fw_driver);
4488c2ecf20Sopenharmony_ci	if (ret)
4498c2ecf20Sopenharmony_ci		i2c_del_driver(&stdp4028_ge_b850v3_fw_driver);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return ret;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_cimodule_init(stdpxxxx_ge_b850v3_init);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic void __exit stdpxxxx_ge_b850v3_exit(void)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	i2c_del_driver(&stdp2690_ge_b850v3_fw_driver);
4588c2ecf20Sopenharmony_ci	i2c_del_driver(&stdp4028_ge_b850v3_fw_driver);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_cimodule_exit(stdpxxxx_ge_b850v3_exit);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Senna Tschudin <peter.senna@collabora.com>");
4638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.co.uk>");
4648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GE LVDS to DP++ display bridge)");
4658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
466