brintos

brintos / linux-shallow public Read only

0
0
Text · 3.9 KiB · a006cb0 Raw
170 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ddbridge-mci.c: Digital Devices microcode interface4 *5 * Copyright (C) 2017-2018 Digital Devices GmbH6 *                         Ralph Metzler <rjkm@metzlerbros.de>7 *                         Marcus Metzler <mocm@metzlerbros.de>8 */9 10#include "ddbridge.h"11#include "ddbridge-io.h"12#include "ddbridge-mci.h"13 14static LIST_HEAD(mci_list);15 16static int mci_reset(struct mci *state)17{18	struct ddb_link *link = state->base->link;19	u32 status = 0;20	u32 timeout = 40;21 22	ddblwritel(link, MCI_CONTROL_RESET, MCI_CONTROL);23	ddblwritel(link, 0, MCI_CONTROL + 4); /* 1= no internal init */24	msleep(300);25	ddblwritel(link, 0, MCI_CONTROL);26 27	while (1) {28		status = ddblreadl(link, MCI_CONTROL);29		if ((status & MCI_CONTROL_READY) == MCI_CONTROL_READY)30			break;31		if (--timeout == 0)32			break;33		msleep(50);34	}35	if ((status & MCI_CONTROL_READY) == 0)36		return -1;37	if (link->ids.device == 0x0009)38		ddblwritel(link, SX8_TSCONFIG_MODE_NORMAL, SX8_TSCONFIG);39	return 0;40}41 42int ddb_mci_config(struct mci *state, u32 config)43{44	struct ddb_link *link = state->base->link;45 46	if (link->ids.device != 0x0009)47		return -EINVAL;48	ddblwritel(link, config, SX8_TSCONFIG);49	return 0;50}51 52static int _mci_cmd_unlocked(struct mci *state,53			     u32 *cmd, u32 cmd_len,54			     u32 *res, u32 res_len)55{56	struct ddb_link *link = state->base->link;57	u32 i, val;58	unsigned long stat;59 60	val = ddblreadl(link, MCI_CONTROL);61	if (val & (MCI_CONTROL_RESET | MCI_CONTROL_START_COMMAND))62		return -EIO;63	if (cmd && cmd_len)64		for (i = 0; i < cmd_len; i++)65			ddblwritel(link, cmd[i], MCI_COMMAND + i * 4);66	val |= (MCI_CONTROL_START_COMMAND | MCI_CONTROL_ENABLE_DONE_INTERRUPT);67	ddblwritel(link, val, MCI_CONTROL);68 69	stat = wait_for_completion_timeout(&state->base->completion, HZ);70	if (stat == 0) {71		dev_warn(state->base->dev, "MCI-%d: MCI timeout\n", state->nr);72		return -EIO;73	}74	if (res && res_len)75		for (i = 0; i < res_len; i++)76			res[i] = ddblreadl(link, MCI_RESULT + i * 4);77	return 0;78}79 80int ddb_mci_cmd(struct mci *state,81		struct mci_command *command,82		struct mci_result *result)83{84	int stat;85 86	mutex_lock(&state->base->mci_lock);87	stat = _mci_cmd_unlocked(state,88				 (u32 *)command, sizeof(*command) / sizeof(u32),89				 (u32 *)result,	sizeof(*result) / sizeof(u32));90	mutex_unlock(&state->base->mci_lock);91	return stat;92}93 94static void mci_handler(void *priv)95{96	struct mci_base *base = (struct mci_base *)priv;97 98	complete(&base->completion);99}100 101static struct mci_base *match_base(void *key)102{103	struct mci_base *p;104 105	list_for_each_entry(p, &mci_list, mci_list)106		if (p->key == key)107			return p;108	return NULL;109}110 111static int probe(struct mci *state)112{113	mci_reset(state);114	return 0;115}116 117struct dvb_frontend118*ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr,119		int (**fn_set_input)(struct dvb_frontend *fe, int input))120{121	struct ddb_port *port = input->port;122	struct ddb *dev = port->dev;123	struct ddb_link *link = &dev->link[port->lnr];124	struct mci_base *base;125	struct mci *state;126	void *key = cfg->type ? (void *)port : (void *)link;127 128	state = kzalloc(cfg->state_size, GFP_KERNEL);129	if (!state)130		return NULL;131 132	base = match_base(key);133	if (base) {134		base->count++;135		state->base = base;136	} else {137		base = kzalloc(cfg->base_size, GFP_KERNEL);138		if (!base)139			goto fail;140		base->key = key;141		base->count = 1;142		base->link = link;143		base->dev = dev->dev;144		mutex_init(&base->mci_lock);145		mutex_init(&base->tuner_lock);146		ddb_irq_set(dev, link->nr, 0, mci_handler, base);147		init_completion(&base->completion);148		state->base = base;149		if (probe(state) < 0) {150			kfree(base);151			goto fail;152		}153		list_add(&base->mci_list, &mci_list);154		if (cfg->base_init)155			cfg->base_init(base);156	}157	memcpy(&state->fe.ops, cfg->fe_ops, sizeof(struct dvb_frontend_ops));158	state->fe.demodulator_priv = state;159	state->nr = nr;160	*fn_set_input = cfg->set_input;161	state->tuner = nr;162	state->demod = nr;163	if (cfg->init)164		cfg->init(state);165	return &state->fe;166fail:167	kfree(state);168	return NULL;169}170