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