162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "radeonfb.h"
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/fb.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/i2c-algo-bit.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/io.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <video/radeon.h>
1662306a36Sopenharmony_ci#include "../edid.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void radeon_gpio_setscl(void* data, int state)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct radeon_i2c_chan 	*chan = data;
2162306a36Sopenharmony_ci	struct radeonfb_info	*rinfo = chan->rinfo;
2262306a36Sopenharmony_ci	u32			val;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	val = INREG(chan->ddc_reg) & ~(VGA_DDC_CLK_OUT_EN);
2562306a36Sopenharmony_ci	if (!state)
2662306a36Sopenharmony_ci		val |= VGA_DDC_CLK_OUT_EN;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	OUTREG(chan->ddc_reg, val);
2962306a36Sopenharmony_ci	(void)INREG(chan->ddc_reg);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void radeon_gpio_setsda(void* data, int state)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct radeon_i2c_chan 	*chan = data;
3562306a36Sopenharmony_ci	struct radeonfb_info	*rinfo = chan->rinfo;
3662306a36Sopenharmony_ci	u32			val;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	val = INREG(chan->ddc_reg) & ~(VGA_DDC_DATA_OUT_EN);
3962306a36Sopenharmony_ci	if (!state)
4062306a36Sopenharmony_ci		val |= VGA_DDC_DATA_OUT_EN;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	OUTREG(chan->ddc_reg, val);
4362306a36Sopenharmony_ci	(void)INREG(chan->ddc_reg);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int radeon_gpio_getscl(void* data)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct radeon_i2c_chan 	*chan = data;
4962306a36Sopenharmony_ci	struct radeonfb_info	*rinfo = chan->rinfo;
5062306a36Sopenharmony_ci	u32			val;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	val = INREG(chan->ddc_reg);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return (val & VGA_DDC_CLK_INPUT) ? 1 : 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int radeon_gpio_getsda(void* data)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct radeon_i2c_chan 	*chan = data;
6062306a36Sopenharmony_ci	struct radeonfb_info	*rinfo = chan->rinfo;
6162306a36Sopenharmony_ci	u32			val;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	val = INREG(chan->ddc_reg);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return (val & VGA_DDC_DATA_INPUT) ? 1 : 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int radeon_setup_i2c_bus(struct radeon_i2c_chan *chan, const char *name)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int rc;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	snprintf(chan->adapter.name, sizeof(chan->adapter.name),
7362306a36Sopenharmony_ci		 "radeonfb %s", name);
7462306a36Sopenharmony_ci	chan->adapter.owner		= THIS_MODULE;
7562306a36Sopenharmony_ci	chan->adapter.algo_data		= &chan->algo;
7662306a36Sopenharmony_ci	chan->adapter.dev.parent	= &chan->rinfo->pdev->dev;
7762306a36Sopenharmony_ci	chan->algo.setsda		= radeon_gpio_setsda;
7862306a36Sopenharmony_ci	chan->algo.setscl		= radeon_gpio_setscl;
7962306a36Sopenharmony_ci	chan->algo.getsda		= radeon_gpio_getsda;
8062306a36Sopenharmony_ci	chan->algo.getscl		= radeon_gpio_getscl;
8162306a36Sopenharmony_ci	chan->algo.udelay		= 10;
8262306a36Sopenharmony_ci	chan->algo.timeout		= 20;
8362306a36Sopenharmony_ci	chan->algo.data 		= chan;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	i2c_set_adapdata(&chan->adapter, chan);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* Raise SCL and SDA */
8862306a36Sopenharmony_ci	radeon_gpio_setsda(chan, 1);
8962306a36Sopenharmony_ci	radeon_gpio_setscl(chan, 1);
9062306a36Sopenharmony_ci	udelay(20);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	rc = i2c_bit_add_bus(&chan->adapter);
9362306a36Sopenharmony_ci	if (rc == 0)
9462306a36Sopenharmony_ci		dev_dbg(&chan->rinfo->pdev->dev, "I2C bus %s registered.\n", name);
9562306a36Sopenharmony_ci	else
9662306a36Sopenharmony_ci		dev_warn(&chan->rinfo->pdev->dev, "Failed to register I2C bus %s.\n", name);
9762306a36Sopenharmony_ci	return rc;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_civoid radeon_create_i2c_busses(struct radeonfb_info *rinfo)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	rinfo->i2c[0].rinfo	= rinfo;
10362306a36Sopenharmony_ci	rinfo->i2c[0].ddc_reg	= GPIO_MONID;
10462306a36Sopenharmony_ci#ifndef CONFIG_PPC
10562306a36Sopenharmony_ci	rinfo->i2c[0].adapter.class = I2C_CLASS_HWMON;
10662306a36Sopenharmony_ci#endif
10762306a36Sopenharmony_ci	radeon_setup_i2c_bus(&rinfo->i2c[0], "monid");
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	rinfo->i2c[1].rinfo	= rinfo;
11062306a36Sopenharmony_ci	rinfo->i2c[1].ddc_reg	= GPIO_DVI_DDC;
11162306a36Sopenharmony_ci	radeon_setup_i2c_bus(&rinfo->i2c[1], "dvi");
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	rinfo->i2c[2].rinfo	= rinfo;
11462306a36Sopenharmony_ci	rinfo->i2c[2].ddc_reg	= GPIO_VGA_DDC;
11562306a36Sopenharmony_ci	radeon_setup_i2c_bus(&rinfo->i2c[2], "vga");
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	rinfo->i2c[3].rinfo	= rinfo;
11862306a36Sopenharmony_ci	rinfo->i2c[3].ddc_reg	= GPIO_CRT2_DDC;
11962306a36Sopenharmony_ci	radeon_setup_i2c_bus(&rinfo->i2c[3], "crt2");
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid radeon_delete_i2c_busses(struct radeonfb_info *rinfo)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	if (rinfo->i2c[0].rinfo)
12562306a36Sopenharmony_ci		i2c_del_adapter(&rinfo->i2c[0].adapter);
12662306a36Sopenharmony_ci	rinfo->i2c[0].rinfo = NULL;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (rinfo->i2c[1].rinfo)
12962306a36Sopenharmony_ci		i2c_del_adapter(&rinfo->i2c[1].adapter);
13062306a36Sopenharmony_ci	rinfo->i2c[1].rinfo = NULL;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (rinfo->i2c[2].rinfo)
13362306a36Sopenharmony_ci		i2c_del_adapter(&rinfo->i2c[2].adapter);
13462306a36Sopenharmony_ci	rinfo->i2c[2].rinfo = NULL;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (rinfo->i2c[3].rinfo)
13762306a36Sopenharmony_ci		i2c_del_adapter(&rinfo->i2c[3].adapter);
13862306a36Sopenharmony_ci	rinfo->i2c[3].rinfo = NULL;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciint radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn,
14262306a36Sopenharmony_ci			       u8 **out_edid)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	u8 *edid;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	edid = fb_ddc_read(&rinfo->i2c[conn-1].adapter);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (out_edid)
14962306a36Sopenharmony_ci		*out_edid = edid;
15062306a36Sopenharmony_ci	if (!edid) {
15162306a36Sopenharmony_ci		pr_debug("radeonfb: I2C (port %d) ... not found\n", conn);
15262306a36Sopenharmony_ci		return MT_NONE;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci	if (edid[0x14] & 0x80) {
15562306a36Sopenharmony_ci		/* Fix detection using BIOS tables */
15662306a36Sopenharmony_ci		if (rinfo->is_mobility /*&& conn == ddc_dvi*/ &&
15762306a36Sopenharmony_ci		    (INREG(LVDS_GEN_CNTL) & LVDS_ON)) {
15862306a36Sopenharmony_ci			pr_debug("radeonfb: I2C (port %d) ... found LVDS panel\n", conn);
15962306a36Sopenharmony_ci			return MT_LCD;
16062306a36Sopenharmony_ci		} else {
16162306a36Sopenharmony_ci			pr_debug("radeonfb: I2C (port %d) ... found TMDS panel\n", conn);
16262306a36Sopenharmony_ci			return MT_DFP;
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci	pr_debug("radeonfb: I2C (port %d) ... found CRT display\n", conn);
16662306a36Sopenharmony_ci	return MT_CRT;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
169