18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  cobalt driver initialization and card probing
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Derived from cx18-driver.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
88c2ecf20Sopenharmony_ci *  All rights reserved.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <media/i2c/adv7604.h>
148c2ecf20Sopenharmony_ci#include <media/i2c/adv7842.h>
158c2ecf20Sopenharmony_ci#include <media/i2c/adv7511.h>
168c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
178c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "cobalt-driver.h"
208c2ecf20Sopenharmony_ci#include "cobalt-irq.h"
218c2ecf20Sopenharmony_ci#include "cobalt-i2c.h"
228c2ecf20Sopenharmony_ci#include "cobalt-v4l2.h"
238c2ecf20Sopenharmony_ci#include "cobalt-flash.h"
248c2ecf20Sopenharmony_ci#include "cobalt-alsa.h"
258c2ecf20Sopenharmony_ci#include "cobalt-omnitek.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* add your revision and whatnot here */
288c2ecf20Sopenharmony_cistatic const struct pci_device_id cobalt_pci_tbl[] = {
298c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_COBALT,
308c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
318c2ecf20Sopenharmony_ci	{0,}
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cobalt_pci_tbl);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic atomic_t cobalt_instance = ATOMIC_INIT(0);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciint cobalt_debug;
398c2ecf20Sopenharmony_cimodule_param_named(debug, cobalt_debug, int, 0644);
408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level. Default: 0\n");
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ciint cobalt_ignore_err;
438c2ecf20Sopenharmony_cimodule_param_named(ignore_err, cobalt_ignore_err, int, 0644);
448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ignore_err,
458c2ecf20Sopenharmony_ci	"If set then ignore missing i2c adapters/receivers. Default: 0\n");
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com> & Morten Hestnes");
488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("cobalt driver");
498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic u8 edid[256] = {
528c2ecf20Sopenharmony_ci	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
538c2ecf20Sopenharmony_ci	0x50, 0x21, 0x32, 0x27, 0x00, 0x00, 0x00, 0x00,
548c2ecf20Sopenharmony_ci	0x22, 0x1a, 0x01, 0x03, 0x80, 0x30, 0x1b, 0x78,
558c2ecf20Sopenharmony_ci	0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,
568c2ecf20Sopenharmony_ci	0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59,
578c2ecf20Sopenharmony_ci	0x45, 0x59, 0x61, 0x59, 0x81, 0x99, 0x01, 0x01,
588c2ecf20Sopenharmony_ci	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
598c2ecf20Sopenharmony_ci	0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
608c2ecf20Sopenharmony_ci	0x46, 0x00, 0xe0, 0x0e, 0x11, 0x00, 0x00, 0x1e,
618c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
628c2ecf20Sopenharmony_ci	0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
638c2ecf20Sopenharmony_ci	0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x63,
648c2ecf20Sopenharmony_ci	0x6f, 0x62, 0x61, 0x6c, 0x74, 0x0a, 0x20, 0x20,
658c2ecf20Sopenharmony_ci	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10,
668c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
678c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x9c,
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	0x02, 0x03, 0x1f, 0xf0, 0x4a, 0x90, 0x1f, 0x04,
708c2ecf20Sopenharmony_ci	0x13, 0x22, 0x21, 0x20, 0x02, 0x11, 0x01, 0x23,
718c2ecf20Sopenharmony_ci	0x09, 0x07, 0x07, 0x68, 0x03, 0x0c, 0x00, 0x10,
728c2ecf20Sopenharmony_ci	0x00, 0x00, 0x22, 0x0f, 0xe2, 0x00, 0xea, 0x00,
738c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
748c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
758c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
768c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
778c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
788c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
798c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
808c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
818c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
828c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
838c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
848c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7,
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic void cobalt_set_interrupt(struct cobalt *cobalt, bool enable)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	if (enable) {
908c2ecf20Sopenharmony_ci		unsigned irqs = COBALT_SYSSTAT_VI0_INT1_MSK |
918c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI1_INT1_MSK |
928c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI2_INT1_MSK |
938c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI3_INT1_MSK |
948c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI0_INT2_MSK |
958c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI1_INT2_MSK |
968c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI2_INT2_MSK |
978c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI3_INT2_MSK |
988c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI0_LOST_DATA_MSK |
998c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI1_LOST_DATA_MSK |
1008c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI2_LOST_DATA_MSK |
1018c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI3_LOST_DATA_MSK |
1028c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_AUD_IN_LOST_DATA_MSK;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		if (cobalt->have_hsma_rx)
1058c2ecf20Sopenharmony_ci			irqs |= COBALT_SYSSTAT_VIHSMA_INT1_MSK |
1068c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VIHSMA_INT2_MSK |
1078c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VIHSMA_LOST_DATA_MSK;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		if (cobalt->have_hsma_tx)
1108c2ecf20Sopenharmony_ci			irqs |= COBALT_SYSSTAT_VOHSMA_INT1_MSK |
1118c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VOHSMA_LOST_DATA_MSK |
1128c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_AUD_OUT_LOST_DATA_MSK;
1138c2ecf20Sopenharmony_ci		/* Clear any existing interrupts */
1148c2ecf20Sopenharmony_ci		cobalt_write_bar1(cobalt, COBALT_SYS_STAT_EDGE, 0xffffffff);
1158c2ecf20Sopenharmony_ci		/* PIO Core interrupt mask register.
1168c2ecf20Sopenharmony_ci		   Enable ADV7604 INT1 interrupts */
1178c2ecf20Sopenharmony_ci		cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, irqs);
1188c2ecf20Sopenharmony_ci	} else {
1198c2ecf20Sopenharmony_ci		/* Disable all ADV7604 interrupts */
1208c2ecf20Sopenharmony_ci		cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, 0);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic unsigned cobalt_get_sd_nr(struct v4l2_subdev *sd)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct cobalt *cobalt = to_cobalt(sd->v4l2_dev);
1278c2ecf20Sopenharmony_ci	unsigned i;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	for (i = 0; i < COBALT_NUM_NODES; i++)
1308c2ecf20Sopenharmony_ci		if (sd == cobalt->streams[i].sd)
1318c2ecf20Sopenharmony_ci			return i;
1328c2ecf20Sopenharmony_ci	cobalt_err("Invalid adv7604 subdev pointer!\n");
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic void cobalt_notify(struct v4l2_subdev *sd,
1378c2ecf20Sopenharmony_ci			  unsigned int notification, void *arg)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct cobalt *cobalt = to_cobalt(sd->v4l2_dev);
1408c2ecf20Sopenharmony_ci	unsigned sd_nr = cobalt_get_sd_nr(sd);
1418c2ecf20Sopenharmony_ci	struct cobalt_stream *s = &cobalt->streams[sd_nr];
1428c2ecf20Sopenharmony_ci	bool hotplug = arg ? *((int *)arg) : false;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (s->is_output)
1458c2ecf20Sopenharmony_ci		return;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	switch (notification) {
1488c2ecf20Sopenharmony_ci	case ADV76XX_HOTPLUG:
1498c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
1508c2ecf20Sopenharmony_ci			COBALT_SYS_CTRL_HPD_TO_CONNECTOR_BIT(sd_nr), hotplug);
1518c2ecf20Sopenharmony_ci		cobalt_dbg(1, "Set hotplug for adv %d to %d\n", sd_nr, hotplug);
1528c2ecf20Sopenharmony_ci		break;
1538c2ecf20Sopenharmony_ci	case V4L2_DEVICE_NOTIFY_EVENT:
1548c2ecf20Sopenharmony_ci		cobalt_dbg(1, "Format changed for adv %d\n", sd_nr);
1558c2ecf20Sopenharmony_ci		v4l2_event_queue(&s->vdev, arg);
1568c2ecf20Sopenharmony_ci		break;
1578c2ecf20Sopenharmony_ci	default:
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int get_payload_size(u16 code)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	switch (code) {
1658c2ecf20Sopenharmony_ci	case 0: return 128;
1668c2ecf20Sopenharmony_ci	case 1: return 256;
1678c2ecf20Sopenharmony_ci	case 2: return 512;
1688c2ecf20Sopenharmony_ci	case 3: return 1024;
1698c2ecf20Sopenharmony_ci	case 4: return 2048;
1708c2ecf20Sopenharmony_ci	case 5: return 4096;
1718c2ecf20Sopenharmony_ci	default: return 0;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic const char *get_link_speed(u16 stat)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	switch (stat & PCI_EXP_LNKSTA_CLS) {
1798c2ecf20Sopenharmony_ci	case 1:	return "2.5 Gbit/s";
1808c2ecf20Sopenharmony_ci	case 2:	return "5 Gbit/s";
1818c2ecf20Sopenharmony_ci	case 3:	return "10 Gbit/s";
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	return "Unknown speed";
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_civoid cobalt_pcie_status_show(struct cobalt *cobalt)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = cobalt->pci_dev;
1898c2ecf20Sopenharmony_ci	struct pci_dev *pci_bus_dev = cobalt->pci_dev->bus->self;
1908c2ecf20Sopenharmony_ci	u32 capa;
1918c2ecf20Sopenharmony_ci	u16 stat, ctrl;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (!pci_is_pcie(pci_dev) || !pci_is_pcie(pci_bus_dev))
1948c2ecf20Sopenharmony_ci		return;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* Device */
1978c2ecf20Sopenharmony_ci	pcie_capability_read_dword(pci_dev, PCI_EXP_DEVCAP, &capa);
1988c2ecf20Sopenharmony_ci	pcie_capability_read_word(pci_dev, PCI_EXP_DEVCTL, &ctrl);
1998c2ecf20Sopenharmony_ci	pcie_capability_read_word(pci_dev, PCI_EXP_DEVSTA, &stat);
2008c2ecf20Sopenharmony_ci	cobalt_info("PCIe device capability 0x%08x: Max payload %d\n",
2018c2ecf20Sopenharmony_ci		    capa, get_payload_size(capa & PCI_EXP_DEVCAP_PAYLOAD));
2028c2ecf20Sopenharmony_ci	cobalt_info("PCIe device control 0x%04x: Max payload %d. Max read request %d\n",
2038c2ecf20Sopenharmony_ci		    ctrl,
2048c2ecf20Sopenharmony_ci		    get_payload_size((ctrl & PCI_EXP_DEVCTL_PAYLOAD) >> 5),
2058c2ecf20Sopenharmony_ci		    get_payload_size((ctrl & PCI_EXP_DEVCTL_READRQ) >> 12));
2068c2ecf20Sopenharmony_ci	cobalt_info("PCIe device status 0x%04x\n", stat);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Link */
2098c2ecf20Sopenharmony_ci	pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &capa);
2108c2ecf20Sopenharmony_ci	pcie_capability_read_word(pci_dev, PCI_EXP_LNKCTL, &ctrl);
2118c2ecf20Sopenharmony_ci	pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &stat);
2128c2ecf20Sopenharmony_ci	cobalt_info("PCIe link capability 0x%08x: %s per lane and %u lanes\n",
2138c2ecf20Sopenharmony_ci			capa, get_link_speed(capa),
2148c2ecf20Sopenharmony_ci			FIELD_GET(PCI_EXP_LNKCAP_MLW, capa));
2158c2ecf20Sopenharmony_ci	cobalt_info("PCIe link control 0x%04x\n", ctrl);
2168c2ecf20Sopenharmony_ci	cobalt_info("PCIe link status 0x%04x: %s per lane and %u lanes\n",
2178c2ecf20Sopenharmony_ci		    stat, get_link_speed(stat),
2188c2ecf20Sopenharmony_ci		    FIELD_GET(PCI_EXP_LNKSTA_NLW, stat));
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* Bus */
2218c2ecf20Sopenharmony_ci	pcie_capability_read_dword(pci_bus_dev, PCI_EXP_LNKCAP, &capa);
2228c2ecf20Sopenharmony_ci	cobalt_info("PCIe bus link capability 0x%08x: %s per lane and %u lanes\n",
2238c2ecf20Sopenharmony_ci			capa, get_link_speed(capa),
2248c2ecf20Sopenharmony_ci			FIELD_GET(PCI_EXP_LNKCAP_MLW, capa));
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Slot */
2278c2ecf20Sopenharmony_ci	pcie_capability_read_dword(pci_dev, PCI_EXP_SLTCAP, &capa);
2288c2ecf20Sopenharmony_ci	pcie_capability_read_word(pci_dev, PCI_EXP_SLTCTL, &ctrl);
2298c2ecf20Sopenharmony_ci	pcie_capability_read_word(pci_dev, PCI_EXP_SLTSTA, &stat);
2308c2ecf20Sopenharmony_ci	cobalt_info("PCIe slot capability 0x%08x\n", capa);
2318c2ecf20Sopenharmony_ci	cobalt_info("PCIe slot control 0x%04x\n", ctrl);
2328c2ecf20Sopenharmony_ci	cobalt_info("PCIe slot status 0x%04x\n", stat);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic unsigned pcie_link_get_lanes(struct cobalt *cobalt)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = cobalt->pci_dev;
2388c2ecf20Sopenharmony_ci	u16 link;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (!pci_is_pcie(pci_dev))
2418c2ecf20Sopenharmony_ci		return 0;
2428c2ecf20Sopenharmony_ci	pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &link);
2438c2ecf20Sopenharmony_ci	return FIELD_GET(PCI_EXP_LNKSTA_NLW, link);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic unsigned pcie_bus_link_get_lanes(struct cobalt *cobalt)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = cobalt->pci_dev->bus->self;
2498c2ecf20Sopenharmony_ci	u32 link;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (!pci_is_pcie(pci_dev))
2528c2ecf20Sopenharmony_ci		return 0;
2538c2ecf20Sopenharmony_ci	pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &link);
2548c2ecf20Sopenharmony_ci	return FIELD_GET(PCI_EXP_LNKCAP_MLW, link);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic void msi_config_show(struct cobalt *cobalt, struct pci_dev *pci_dev)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	u16 ctrl, data;
2608c2ecf20Sopenharmony_ci	u32 adrs_l, adrs_h;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	pci_read_config_word(pci_dev, 0x52, &ctrl);
2638c2ecf20Sopenharmony_ci	cobalt_info("MSI %s\n", ctrl & 1 ? "enable" : "disable");
2648c2ecf20Sopenharmony_ci	cobalt_info("MSI multiple message: Capable %u. Enable %u\n",
2658c2ecf20Sopenharmony_ci		    (1 << ((ctrl >> 1) & 7)), (1 << ((ctrl >> 4) & 7)));
2668c2ecf20Sopenharmony_ci	if (ctrl & 0x80)
2678c2ecf20Sopenharmony_ci		cobalt_info("MSI: 64-bit address capable\n");
2688c2ecf20Sopenharmony_ci	pci_read_config_dword(pci_dev, 0x54, &adrs_l);
2698c2ecf20Sopenharmony_ci	pci_read_config_dword(pci_dev, 0x58, &adrs_h);
2708c2ecf20Sopenharmony_ci	pci_read_config_word(pci_dev, 0x5c, &data);
2718c2ecf20Sopenharmony_ci	if (ctrl & 0x80)
2728c2ecf20Sopenharmony_ci		cobalt_info("MSI: Address 0x%08x%08x. Data 0x%04x\n",
2738c2ecf20Sopenharmony_ci				adrs_h, adrs_l, data);
2748c2ecf20Sopenharmony_ci	else
2758c2ecf20Sopenharmony_ci		cobalt_info("MSI: Address 0x%08x. Data 0x%04x\n",
2768c2ecf20Sopenharmony_ci				adrs_l, data);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void cobalt_pci_iounmap(struct cobalt *cobalt, struct pci_dev *pci_dev)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	if (cobalt->bar0) {
2828c2ecf20Sopenharmony_ci		pci_iounmap(pci_dev, cobalt->bar0);
2838c2ecf20Sopenharmony_ci		cobalt->bar0 = NULL;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci	if (cobalt->bar1) {
2868c2ecf20Sopenharmony_ci		pci_iounmap(pci_dev, cobalt->bar1);
2878c2ecf20Sopenharmony_ci		cobalt->bar1 = NULL;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void cobalt_free_msi(struct cobalt *cobalt, struct pci_dev *pci_dev)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	free_irq(pci_dev->irq, (void *)cobalt);
2948c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pci_dev);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int cobalt_setup_pci(struct cobalt *cobalt, struct pci_dev *pci_dev,
2988c2ecf20Sopenharmony_ci			    const struct pci_device_id *pci_id)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	u32 ctrl;
3018c2ecf20Sopenharmony_ci	int ret;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	cobalt_dbg(1, "enabling pci device\n");
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	ret = pci_enable_device(pci_dev);
3068c2ecf20Sopenharmony_ci	if (ret) {
3078c2ecf20Sopenharmony_ci		cobalt_err("can't enable device\n");
3088c2ecf20Sopenharmony_ci		return ret;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	pci_set_master(pci_dev);
3118c2ecf20Sopenharmony_ci	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &cobalt->card_rev);
3128c2ecf20Sopenharmony_ci	pci_read_config_word(pci_dev, PCI_DEVICE_ID, &cobalt->device_id);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	switch (cobalt->device_id) {
3158c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_COBALT:
3168c2ecf20Sopenharmony_ci		cobalt_info("PCI Express interface from Omnitek\n");
3178c2ecf20Sopenharmony_ci		break;
3188c2ecf20Sopenharmony_ci	default:
3198c2ecf20Sopenharmony_ci		cobalt_info("PCI Express interface provider is unknown!\n");
3208c2ecf20Sopenharmony_ci		break;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (pcie_link_get_lanes(cobalt) != 8) {
3248c2ecf20Sopenharmony_ci		cobalt_warn("PCI Express link width is %d lanes.\n",
3258c2ecf20Sopenharmony_ci				pcie_link_get_lanes(cobalt));
3268c2ecf20Sopenharmony_ci		if (pcie_bus_link_get_lanes(cobalt) < 8)
3278c2ecf20Sopenharmony_ci			cobalt_warn("The current slot only supports %d lanes, for best performance 8 are needed\n",
3288c2ecf20Sopenharmony_ci					pcie_bus_link_get_lanes(cobalt));
3298c2ecf20Sopenharmony_ci		if (pcie_link_get_lanes(cobalt) != pcie_bus_link_get_lanes(cobalt)) {
3308c2ecf20Sopenharmony_ci			cobalt_err("The card is most likely not seated correctly in the PCIe slot\n");
3318c2ecf20Sopenharmony_ci			ret = -EIO;
3328c2ecf20Sopenharmony_ci			goto err_disable;
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64))) {
3378c2ecf20Sopenharmony_ci		ret = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
3388c2ecf20Sopenharmony_ci		if (ret) {
3398c2ecf20Sopenharmony_ci			cobalt_err("no suitable DMA available\n");
3408c2ecf20Sopenharmony_ci			goto err_disable;
3418c2ecf20Sopenharmony_ci		}
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	ret = pci_request_regions(pci_dev, "cobalt");
3458c2ecf20Sopenharmony_ci	if (ret) {
3468c2ecf20Sopenharmony_ci		cobalt_err("error requesting regions\n");
3478c2ecf20Sopenharmony_ci		goto err_disable;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	cobalt_pcie_status_show(cobalt);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	cobalt->bar0 = pci_iomap(pci_dev, 0, 0);
3538c2ecf20Sopenharmony_ci	cobalt->bar1 = pci_iomap(pci_dev, 1, 0);
3548c2ecf20Sopenharmony_ci	if (cobalt->bar1 == NULL) {
3558c2ecf20Sopenharmony_ci		cobalt->bar1 = pci_iomap(pci_dev, 2, 0);
3568c2ecf20Sopenharmony_ci		cobalt_info("64-bit BAR\n");
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci	if (!cobalt->bar0 || !cobalt->bar1) {
3598c2ecf20Sopenharmony_ci		ret = -EIO;
3608c2ecf20Sopenharmony_ci		goto err_release;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* Reset the video inputs before enabling any interrupts */
3648c2ecf20Sopenharmony_ci	ctrl = cobalt_read_bar1(cobalt, COBALT_SYS_CTRL_BASE);
3658c2ecf20Sopenharmony_ci	cobalt_write_bar1(cobalt, COBALT_SYS_CTRL_BASE, ctrl & ~0xf00);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* Disable interrupts to prevent any spurious interrupts
3688c2ecf20Sopenharmony_ci	   from being generated. */
3698c2ecf20Sopenharmony_ci	cobalt_set_interrupt(cobalt, false);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (pci_alloc_irq_vectors(pci_dev, 1, 1, PCI_IRQ_MSI) < 1) {
3728c2ecf20Sopenharmony_ci		cobalt_err("Could not enable MSI\n");
3738c2ecf20Sopenharmony_ci		ret = -EIO;
3748c2ecf20Sopenharmony_ci		goto err_release;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci	msi_config_show(cobalt, pci_dev);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* Register IRQ */
3798c2ecf20Sopenharmony_ci	if (request_irq(pci_dev->irq, cobalt_irq_handler, IRQF_SHARED,
3808c2ecf20Sopenharmony_ci			cobalt->v4l2_dev.name, (void *)cobalt)) {
3818c2ecf20Sopenharmony_ci		cobalt_err("Failed to register irq %d\n", pci_dev->irq);
3828c2ecf20Sopenharmony_ci		ret = -EIO;
3838c2ecf20Sopenharmony_ci		goto err_msi;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	omni_sg_dma_init(cobalt);
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cierr_msi:
3908c2ecf20Sopenharmony_ci	pci_disable_msi(pci_dev);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cierr_release:
3938c2ecf20Sopenharmony_ci	cobalt_pci_iounmap(cobalt, pci_dev);
3948c2ecf20Sopenharmony_ci	pci_release_regions(pci_dev);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cierr_disable:
3978c2ecf20Sopenharmony_ci	pci_disable_device(cobalt->pci_dev);
3988c2ecf20Sopenharmony_ci	return ret;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic int cobalt_hdl_info_get(struct cobalt *cobalt)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	int i;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	for (i = 0; i < COBALT_HDL_INFO_SIZE; i++)
4068c2ecf20Sopenharmony_ci		cobalt->hdl_info[i] =
4078c2ecf20Sopenharmony_ci			ioread8(cobalt->bar1 + COBALT_HDL_INFO_BASE + i);
4088c2ecf20Sopenharmony_ci	cobalt->hdl_info[COBALT_HDL_INFO_SIZE - 1] = '\0';
4098c2ecf20Sopenharmony_ci	if (strstr(cobalt->hdl_info, COBALT_HDL_SEARCH_STR))
4108c2ecf20Sopenharmony_ci		return 0;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return 1;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic void cobalt_stream_struct_init(struct cobalt *cobalt)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	int i;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	for (i = 0; i < COBALT_NUM_STREAMS; i++) {
4208c2ecf20Sopenharmony_ci		struct cobalt_stream *s = &cobalt->streams[i];
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		s->cobalt = cobalt;
4238c2ecf20Sopenharmony_ci		s->flags = 0;
4248c2ecf20Sopenharmony_ci		s->is_audio = false;
4258c2ecf20Sopenharmony_ci		s->is_output = false;
4268c2ecf20Sopenharmony_ci		s->is_dummy = true;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		/* The Memory DMA channels will always get a lower channel
4298c2ecf20Sopenharmony_ci		 * number than the FIFO DMA. Video input should map to the
4308c2ecf20Sopenharmony_ci		 * stream 0-3. The other can use stream struct from 4 and
4318c2ecf20Sopenharmony_ci		 * higher */
4328c2ecf20Sopenharmony_ci		if (i <= COBALT_HSMA_IN_NODE) {
4338c2ecf20Sopenharmony_ci			s->dma_channel = i + cobalt->first_fifo_channel;
4348c2ecf20Sopenharmony_ci			s->video_channel = i;
4358c2ecf20Sopenharmony_ci			s->dma_fifo_mask =
4368c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI0_LOST_DATA_MSK << (4 * i);
4378c2ecf20Sopenharmony_ci			s->adv_irq_mask =
4388c2ecf20Sopenharmony_ci				COBALT_SYSSTAT_VI0_INT1_MSK << (4 * i);
4398c2ecf20Sopenharmony_ci		} else if (i >= COBALT_AUDIO_IN_STREAM &&
4408c2ecf20Sopenharmony_ci			   i <= COBALT_AUDIO_IN_STREAM + 4) {
4418c2ecf20Sopenharmony_ci			unsigned idx = i - COBALT_AUDIO_IN_STREAM;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci			s->dma_channel = 6 + idx;
4448c2ecf20Sopenharmony_ci			s->is_audio = true;
4458c2ecf20Sopenharmony_ci			s->video_channel = idx;
4468c2ecf20Sopenharmony_ci			s->dma_fifo_mask = COBALT_SYSSTAT_AUD_IN_LOST_DATA_MSK;
4478c2ecf20Sopenharmony_ci		} else if (i == COBALT_HSMA_OUT_NODE) {
4488c2ecf20Sopenharmony_ci			s->dma_channel = 11;
4498c2ecf20Sopenharmony_ci			s->is_output = true;
4508c2ecf20Sopenharmony_ci			s->video_channel = 5;
4518c2ecf20Sopenharmony_ci			s->dma_fifo_mask = COBALT_SYSSTAT_VOHSMA_LOST_DATA_MSK;
4528c2ecf20Sopenharmony_ci			s->adv_irq_mask = COBALT_SYSSTAT_VOHSMA_INT1_MSK;
4538c2ecf20Sopenharmony_ci		} else if (i == COBALT_AUDIO_OUT_STREAM) {
4548c2ecf20Sopenharmony_ci			s->dma_channel = 12;
4558c2ecf20Sopenharmony_ci			s->is_audio = true;
4568c2ecf20Sopenharmony_ci			s->is_output = true;
4578c2ecf20Sopenharmony_ci			s->video_channel = 5;
4588c2ecf20Sopenharmony_ci			s->dma_fifo_mask = COBALT_SYSSTAT_AUD_OUT_LOST_DATA_MSK;
4598c2ecf20Sopenharmony_ci		} else {
4608c2ecf20Sopenharmony_ci			/* FIXME: Memory DMA for debug purpose */
4618c2ecf20Sopenharmony_ci			s->dma_channel = i - COBALT_NUM_NODES;
4628c2ecf20Sopenharmony_ci		}
4638c2ecf20Sopenharmony_ci		cobalt_info("stream #%d -> dma channel #%d <- video channel %d\n",
4648c2ecf20Sopenharmony_ci			    i, s->dma_channel, s->video_channel);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic int cobalt_subdevs_init(struct cobalt *cobalt)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	static struct adv76xx_platform_data adv7604_pdata = {
4718c2ecf20Sopenharmony_ci		.disable_pwrdnb = 1,
4728c2ecf20Sopenharmony_ci		.ain_sel = ADV7604_AIN7_8_9_NC_SYNC_3_1,
4738c2ecf20Sopenharmony_ci		.bus_order = ADV7604_BUS_ORDER_BRG,
4748c2ecf20Sopenharmony_ci		.blank_data = 1,
4758c2ecf20Sopenharmony_ci		.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0,
4768c2ecf20Sopenharmony_ci		.int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH,
4778c2ecf20Sopenharmony_ci		.dr_str_data = ADV76XX_DR_STR_HIGH,
4788c2ecf20Sopenharmony_ci		.dr_str_clk = ADV76XX_DR_STR_HIGH,
4798c2ecf20Sopenharmony_ci		.dr_str_sync = ADV76XX_DR_STR_HIGH,
4808c2ecf20Sopenharmony_ci		.hdmi_free_run_mode = 1,
4818c2ecf20Sopenharmony_ci		.inv_vs_pol = 1,
4828c2ecf20Sopenharmony_ci		.inv_hs_pol = 1,
4838c2ecf20Sopenharmony_ci	};
4848c2ecf20Sopenharmony_ci	static struct i2c_board_info adv7604_info = {
4858c2ecf20Sopenharmony_ci		.type = "adv7604",
4868c2ecf20Sopenharmony_ci		.addr = 0x20,
4878c2ecf20Sopenharmony_ci		.platform_data = &adv7604_pdata,
4888c2ecf20Sopenharmony_ci	};
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	struct cobalt_stream *s = cobalt->streams;
4918c2ecf20Sopenharmony_ci	int i;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	for (i = 0; i < COBALT_NUM_INPUTS; i++) {
4948c2ecf20Sopenharmony_ci		struct v4l2_subdev_format sd_fmt = {
4958c2ecf20Sopenharmony_ci			.pad = ADV7604_PAD_SOURCE,
4968c2ecf20Sopenharmony_ci			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
4978c2ecf20Sopenharmony_ci			.format.code = MEDIA_BUS_FMT_YUYV8_1X16,
4988c2ecf20Sopenharmony_ci		};
4998c2ecf20Sopenharmony_ci		struct v4l2_subdev_edid cobalt_edid = {
5008c2ecf20Sopenharmony_ci			.pad = ADV76XX_PAD_HDMI_PORT_A,
5018c2ecf20Sopenharmony_ci			.start_block = 0,
5028c2ecf20Sopenharmony_ci			.blocks = 2,
5038c2ecf20Sopenharmony_ci			.edid = edid,
5048c2ecf20Sopenharmony_ci		};
5058c2ecf20Sopenharmony_ci		int err;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		s[i].pad_source = ADV7604_PAD_SOURCE;
5088c2ecf20Sopenharmony_ci		s[i].i2c_adap = &cobalt->i2c_adap[i];
5098c2ecf20Sopenharmony_ci		if (s[i].i2c_adap->dev.parent == NULL)
5108c2ecf20Sopenharmony_ci			continue;
5118c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
5128c2ecf20Sopenharmony_ci				COBALT_SYS_CTRL_NRESET_TO_HDMI_BIT(i), 1);
5138c2ecf20Sopenharmony_ci		s[i].sd = v4l2_i2c_new_subdev_board(&cobalt->v4l2_dev,
5148c2ecf20Sopenharmony_ci			s[i].i2c_adap, &adv7604_info, NULL);
5158c2ecf20Sopenharmony_ci		if (!s[i].sd) {
5168c2ecf20Sopenharmony_ci			if (cobalt_ignore_err)
5178c2ecf20Sopenharmony_ci				continue;
5188c2ecf20Sopenharmony_ci			return -ENODEV;
5198c2ecf20Sopenharmony_ci		}
5208c2ecf20Sopenharmony_ci		err = v4l2_subdev_call(s[i].sd, video, s_routing,
5218c2ecf20Sopenharmony_ci				ADV76XX_PAD_HDMI_PORT_A, 0, 0);
5228c2ecf20Sopenharmony_ci		if (err)
5238c2ecf20Sopenharmony_ci			return err;
5248c2ecf20Sopenharmony_ci		err = v4l2_subdev_call(s[i].sd, pad, set_edid,
5258c2ecf20Sopenharmony_ci				&cobalt_edid);
5268c2ecf20Sopenharmony_ci		if (err)
5278c2ecf20Sopenharmony_ci			return err;
5288c2ecf20Sopenharmony_ci		err = v4l2_subdev_call(s[i].sd, pad, set_fmt, NULL,
5298c2ecf20Sopenharmony_ci				&sd_fmt);
5308c2ecf20Sopenharmony_ci		if (err)
5318c2ecf20Sopenharmony_ci			return err;
5328c2ecf20Sopenharmony_ci		/* Reset channel video module */
5338c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
5348c2ecf20Sopenharmony_ci				COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(i), 0);
5358c2ecf20Sopenharmony_ci		mdelay(2);
5368c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
5378c2ecf20Sopenharmony_ci				COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(i), 1);
5388c2ecf20Sopenharmony_ci		mdelay(1);
5398c2ecf20Sopenharmony_ci		s[i].is_dummy = false;
5408c2ecf20Sopenharmony_ci		cobalt->streams[i + COBALT_AUDIO_IN_STREAM].is_dummy = false;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci	return 0;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic int cobalt_subdevs_hsma_init(struct cobalt *cobalt)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	static struct adv7842_platform_data adv7842_pdata = {
5488c2ecf20Sopenharmony_ci		.disable_pwrdnb = 1,
5498c2ecf20Sopenharmony_ci		.ain_sel = ADV7842_AIN1_2_3_NC_SYNC_1_2,
5508c2ecf20Sopenharmony_ci		.bus_order = ADV7842_BUS_ORDER_RBG,
5518c2ecf20Sopenharmony_ci		.op_format_mode_sel = ADV7842_OP_FORMAT_MODE0,
5528c2ecf20Sopenharmony_ci		.blank_data = 1,
5538c2ecf20Sopenharmony_ci		.dr_str_data = 3,
5548c2ecf20Sopenharmony_ci		.dr_str_clk = 3,
5558c2ecf20Sopenharmony_ci		.dr_str_sync = 3,
5568c2ecf20Sopenharmony_ci		.mode = ADV7842_MODE_HDMI,
5578c2ecf20Sopenharmony_ci		.hdmi_free_run_enable = 1,
5588c2ecf20Sopenharmony_ci		.vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P,
5598c2ecf20Sopenharmony_ci		.i2c_sdp_io = 0x4a,
5608c2ecf20Sopenharmony_ci		.i2c_sdp = 0x48,
5618c2ecf20Sopenharmony_ci		.i2c_cp = 0x22,
5628c2ecf20Sopenharmony_ci		.i2c_vdp = 0x24,
5638c2ecf20Sopenharmony_ci		.i2c_afe = 0x26,
5648c2ecf20Sopenharmony_ci		.i2c_hdmi = 0x34,
5658c2ecf20Sopenharmony_ci		.i2c_repeater = 0x32,
5668c2ecf20Sopenharmony_ci		.i2c_edid = 0x36,
5678c2ecf20Sopenharmony_ci		.i2c_infoframe = 0x3e,
5688c2ecf20Sopenharmony_ci		.i2c_cec = 0x40,
5698c2ecf20Sopenharmony_ci		.i2c_avlink = 0x42,
5708c2ecf20Sopenharmony_ci	};
5718c2ecf20Sopenharmony_ci	static struct i2c_board_info adv7842_info = {
5728c2ecf20Sopenharmony_ci		.type = "adv7842",
5738c2ecf20Sopenharmony_ci		.addr = 0x20,
5748c2ecf20Sopenharmony_ci		.platform_data = &adv7842_pdata,
5758c2ecf20Sopenharmony_ci	};
5768c2ecf20Sopenharmony_ci	static struct v4l2_subdev_format sd_fmt = {
5778c2ecf20Sopenharmony_ci		.pad = ADV7842_PAD_SOURCE,
5788c2ecf20Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
5798c2ecf20Sopenharmony_ci		.format.code = MEDIA_BUS_FMT_YUYV8_1X16,
5808c2ecf20Sopenharmony_ci	};
5818c2ecf20Sopenharmony_ci	static struct adv7511_platform_data adv7511_pdata = {
5828c2ecf20Sopenharmony_ci		.i2c_edid = 0x7e >> 1,
5838c2ecf20Sopenharmony_ci		.i2c_cec = 0x7c >> 1,
5848c2ecf20Sopenharmony_ci		.i2c_pktmem = 0x70 >> 1,
5858c2ecf20Sopenharmony_ci		.cec_clk = 12000000,
5868c2ecf20Sopenharmony_ci	};
5878c2ecf20Sopenharmony_ci	static struct i2c_board_info adv7511_info = {
5888c2ecf20Sopenharmony_ci		.type = "adv7511-v4l2",
5898c2ecf20Sopenharmony_ci		.addr = 0x39, /* 0x39 or 0x3d */
5908c2ecf20Sopenharmony_ci		.platform_data = &adv7511_pdata,
5918c2ecf20Sopenharmony_ci	};
5928c2ecf20Sopenharmony_ci	struct v4l2_subdev_edid cobalt_edid = {
5938c2ecf20Sopenharmony_ci		.pad = ADV7842_EDID_PORT_A,
5948c2ecf20Sopenharmony_ci		.start_block = 0,
5958c2ecf20Sopenharmony_ci		.blocks = 2,
5968c2ecf20Sopenharmony_ci		.edid = edid,
5978c2ecf20Sopenharmony_ci	};
5988c2ecf20Sopenharmony_ci	struct cobalt_stream *s = &cobalt->streams[COBALT_HSMA_IN_NODE];
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	s->i2c_adap = &cobalt->i2c_adap[COBALT_NUM_ADAPTERS - 1];
6018c2ecf20Sopenharmony_ci	if (s->i2c_adap->dev.parent == NULL)
6028c2ecf20Sopenharmony_ci		return 0;
6038c2ecf20Sopenharmony_ci	cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_NRESET_TO_HDMI_BIT(4), 1);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	s->sd = v4l2_i2c_new_subdev_board(&cobalt->v4l2_dev,
6068c2ecf20Sopenharmony_ci			s->i2c_adap, &adv7842_info, NULL);
6078c2ecf20Sopenharmony_ci	if (s->sd) {
6088c2ecf20Sopenharmony_ci		int err = v4l2_subdev_call(s->sd, pad, set_edid, &cobalt_edid);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci		if (err)
6118c2ecf20Sopenharmony_ci			return err;
6128c2ecf20Sopenharmony_ci		err = v4l2_subdev_call(s->sd, pad, set_fmt, NULL,
6138c2ecf20Sopenharmony_ci				&sd_fmt);
6148c2ecf20Sopenharmony_ci		if (err)
6158c2ecf20Sopenharmony_ci			return err;
6168c2ecf20Sopenharmony_ci		cobalt->have_hsma_rx = true;
6178c2ecf20Sopenharmony_ci		s->pad_source = ADV7842_PAD_SOURCE;
6188c2ecf20Sopenharmony_ci		s->is_dummy = false;
6198c2ecf20Sopenharmony_ci		cobalt->streams[4 + COBALT_AUDIO_IN_STREAM].is_dummy = false;
6208c2ecf20Sopenharmony_ci		/* Reset channel video module */
6218c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
6228c2ecf20Sopenharmony_ci				COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(4), 0);
6238c2ecf20Sopenharmony_ci		mdelay(2);
6248c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
6258c2ecf20Sopenharmony_ci				COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(4), 1);
6268c2ecf20Sopenharmony_ci		mdelay(1);
6278c2ecf20Sopenharmony_ci		return err;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci	cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_NRESET_TO_HDMI_BIT(4), 0);
6308c2ecf20Sopenharmony_ci	cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_PWRDN0_TO_HSMA_TX_BIT, 0);
6318c2ecf20Sopenharmony_ci	s++;
6328c2ecf20Sopenharmony_ci	s->i2c_adap = &cobalt->i2c_adap[COBALT_NUM_ADAPTERS - 1];
6338c2ecf20Sopenharmony_ci	s->sd = v4l2_i2c_new_subdev_board(&cobalt->v4l2_dev,
6348c2ecf20Sopenharmony_ci			s->i2c_adap, &adv7511_info, NULL);
6358c2ecf20Sopenharmony_ci	if (s->sd) {
6368c2ecf20Sopenharmony_ci		/* A transmitter is hooked up, so we can set this bit */
6378c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
6388c2ecf20Sopenharmony_ci				COBALT_SYS_CTRL_HSMA_TX_ENABLE_BIT, 1);
6398c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
6408c2ecf20Sopenharmony_ci				COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(4), 0);
6418c2ecf20Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
6428c2ecf20Sopenharmony_ci				COBALT_SYS_CTRL_VIDEO_TX_RESETN_BIT, 1);
6438c2ecf20Sopenharmony_ci		cobalt->have_hsma_tx = true;
6448c2ecf20Sopenharmony_ci		v4l2_subdev_call(s->sd, core, s_power, 1);
6458c2ecf20Sopenharmony_ci		v4l2_subdev_call(s->sd, video, s_stream, 1);
6468c2ecf20Sopenharmony_ci		v4l2_subdev_call(s->sd, audio, s_stream, 1);
6478c2ecf20Sopenharmony_ci		v4l2_ctrl_s_ctrl(v4l2_ctrl_find(s->sd->ctrl_handler,
6488c2ecf20Sopenharmony_ci				 V4L2_CID_DV_TX_MODE), V4L2_DV_TX_MODE_HDMI);
6498c2ecf20Sopenharmony_ci		s->is_dummy = false;
6508c2ecf20Sopenharmony_ci		cobalt->streams[COBALT_AUDIO_OUT_STREAM].is_dummy = false;
6518c2ecf20Sopenharmony_ci		return 0;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci	return -ENODEV;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic int cobalt_probe(struct pci_dev *pci_dev,
6578c2ecf20Sopenharmony_ci				  const struct pci_device_id *pci_id)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	struct cobalt *cobalt;
6608c2ecf20Sopenharmony_ci	int retval = 0;
6618c2ecf20Sopenharmony_ci	int i;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* FIXME - module parameter arrays constrain max instances */
6648c2ecf20Sopenharmony_ci	i = atomic_inc_return(&cobalt_instance) - 1;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	cobalt = kzalloc(sizeof(struct cobalt), GFP_KERNEL);
6678c2ecf20Sopenharmony_ci	if (cobalt == NULL)
6688c2ecf20Sopenharmony_ci		return -ENOMEM;
6698c2ecf20Sopenharmony_ci	cobalt->pci_dev = pci_dev;
6708c2ecf20Sopenharmony_ci	cobalt->instance = i;
6718c2ecf20Sopenharmony_ci	mutex_init(&cobalt->pci_lock);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	retval = v4l2_device_register(&pci_dev->dev, &cobalt->v4l2_dev);
6748c2ecf20Sopenharmony_ci	if (retval) {
6758c2ecf20Sopenharmony_ci		pr_err("cobalt: v4l2_device_register of card %d failed\n",
6768c2ecf20Sopenharmony_ci				cobalt->instance);
6778c2ecf20Sopenharmony_ci		kfree(cobalt);
6788c2ecf20Sopenharmony_ci		return retval;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci	snprintf(cobalt->v4l2_dev.name, sizeof(cobalt->v4l2_dev.name),
6818c2ecf20Sopenharmony_ci		 "cobalt-%d", cobalt->instance);
6828c2ecf20Sopenharmony_ci	cobalt->v4l2_dev.notify = cobalt_notify;
6838c2ecf20Sopenharmony_ci	cobalt_info("Initializing card %d\n", cobalt->instance);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	cobalt->irq_work_queues =
6868c2ecf20Sopenharmony_ci		create_singlethread_workqueue(cobalt->v4l2_dev.name);
6878c2ecf20Sopenharmony_ci	if (cobalt->irq_work_queues == NULL) {
6888c2ecf20Sopenharmony_ci		cobalt_err("Could not create workqueue\n");
6898c2ecf20Sopenharmony_ci		retval = -ENOMEM;
6908c2ecf20Sopenharmony_ci		goto err;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	INIT_WORK(&cobalt->irq_work_queue, cobalt_irq_work_handler);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* PCI Device Setup */
6968c2ecf20Sopenharmony_ci	retval = cobalt_setup_pci(cobalt, pci_dev, pci_id);
6978c2ecf20Sopenharmony_ci	if (retval != 0)
6988c2ecf20Sopenharmony_ci		goto err_wq;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	/* Show HDL version info */
7018c2ecf20Sopenharmony_ci	if (cobalt_hdl_info_get(cobalt))
7028c2ecf20Sopenharmony_ci		cobalt_info("Not able to read the HDL info\n");
7038c2ecf20Sopenharmony_ci	else
7048c2ecf20Sopenharmony_ci		cobalt_info("%s", cobalt->hdl_info);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	retval = cobalt_i2c_init(cobalt);
7078c2ecf20Sopenharmony_ci	if (retval)
7088c2ecf20Sopenharmony_ci		goto err_pci;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	cobalt_stream_struct_init(cobalt);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	retval = cobalt_subdevs_init(cobalt);
7138c2ecf20Sopenharmony_ci	if (retval)
7148c2ecf20Sopenharmony_ci		goto err_i2c;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	if (!(cobalt_read_bar1(cobalt, COBALT_SYS_STAT_BASE) &
7178c2ecf20Sopenharmony_ci			COBALT_SYSSTAT_HSMA_PRSNTN_MSK)) {
7188c2ecf20Sopenharmony_ci		retval = cobalt_subdevs_hsma_init(cobalt);
7198c2ecf20Sopenharmony_ci		if (retval)
7208c2ecf20Sopenharmony_ci			goto err_i2c;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	retval = cobalt_nodes_register(cobalt);
7248c2ecf20Sopenharmony_ci	if (retval) {
7258c2ecf20Sopenharmony_ci		cobalt_err("Error %d registering device nodes\n", retval);
7268c2ecf20Sopenharmony_ci		goto err_i2c;
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci	cobalt_set_interrupt(cobalt, true);
7298c2ecf20Sopenharmony_ci	v4l2_device_call_all(&cobalt->v4l2_dev, 0, core,
7308c2ecf20Sopenharmony_ci					interrupt_service_routine, 0, NULL);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	cobalt_info("Initialized cobalt card\n");
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	cobalt_flash_probe(cobalt);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	return 0;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cierr_i2c:
7398c2ecf20Sopenharmony_ci	cobalt_i2c_exit(cobalt);
7408c2ecf20Sopenharmony_ci	cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_HSMA_TX_ENABLE_BIT, 0);
7418c2ecf20Sopenharmony_cierr_pci:
7428c2ecf20Sopenharmony_ci	cobalt_free_msi(cobalt, pci_dev);
7438c2ecf20Sopenharmony_ci	cobalt_pci_iounmap(cobalt, pci_dev);
7448c2ecf20Sopenharmony_ci	pci_release_regions(cobalt->pci_dev);
7458c2ecf20Sopenharmony_ci	pci_disable_device(cobalt->pci_dev);
7468c2ecf20Sopenharmony_cierr_wq:
7478c2ecf20Sopenharmony_ci	destroy_workqueue(cobalt->irq_work_queues);
7488c2ecf20Sopenharmony_cierr:
7498c2ecf20Sopenharmony_ci	cobalt_err("error %d on initialization\n", retval);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	v4l2_device_unregister(&cobalt->v4l2_dev);
7528c2ecf20Sopenharmony_ci	kfree(cobalt);
7538c2ecf20Sopenharmony_ci	return retval;
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic void cobalt_remove(struct pci_dev *pci_dev)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
7598c2ecf20Sopenharmony_ci	struct cobalt *cobalt = to_cobalt(v4l2_dev);
7608c2ecf20Sopenharmony_ci	int i;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	cobalt_flash_remove(cobalt);
7638c2ecf20Sopenharmony_ci	cobalt_set_interrupt(cobalt, false);
7648c2ecf20Sopenharmony_ci	flush_workqueue(cobalt->irq_work_queues);
7658c2ecf20Sopenharmony_ci	cobalt_nodes_unregister(cobalt);
7668c2ecf20Sopenharmony_ci	for (i = 0; i < COBALT_NUM_ADAPTERS; i++) {
7678c2ecf20Sopenharmony_ci		struct v4l2_subdev *sd = cobalt->streams[i].sd;
7688c2ecf20Sopenharmony_ci		struct i2c_client *client;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci		if (sd == NULL)
7718c2ecf20Sopenharmony_ci			continue;
7728c2ecf20Sopenharmony_ci		client = v4l2_get_subdevdata(sd);
7738c2ecf20Sopenharmony_ci		v4l2_device_unregister_subdev(sd);
7748c2ecf20Sopenharmony_ci		i2c_unregister_device(client);
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci	cobalt_i2c_exit(cobalt);
7778c2ecf20Sopenharmony_ci	cobalt_free_msi(cobalt, pci_dev);
7788c2ecf20Sopenharmony_ci	cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_HSMA_TX_ENABLE_BIT, 0);
7798c2ecf20Sopenharmony_ci	cobalt_pci_iounmap(cobalt, pci_dev);
7808c2ecf20Sopenharmony_ci	pci_release_regions(cobalt->pci_dev);
7818c2ecf20Sopenharmony_ci	pci_disable_device(cobalt->pci_dev);
7828c2ecf20Sopenharmony_ci	destroy_workqueue(cobalt->irq_work_queues);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	cobalt_info("removed cobalt card\n");
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	v4l2_device_unregister(v4l2_dev);
7878c2ecf20Sopenharmony_ci	kfree(cobalt);
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci/* define a pci_driver for card detection */
7918c2ecf20Sopenharmony_cistatic struct pci_driver cobalt_pci_driver = {
7928c2ecf20Sopenharmony_ci	.name =     "cobalt",
7938c2ecf20Sopenharmony_ci	.id_table = cobalt_pci_tbl,
7948c2ecf20Sopenharmony_ci	.probe =    cobalt_probe,
7958c2ecf20Sopenharmony_ci	.remove =   cobalt_remove,
7968c2ecf20Sopenharmony_ci};
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cimodule_pci_driver(cobalt_pci_driver);
799