18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ddbridge-sx8.c: Digital Devices MAX SX8 driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Digital Devices GmbH
68c2ecf20Sopenharmony_ci *                    Marcus Metzler <mocm@metzlerbros.de>
78c2ecf20Sopenharmony_ci *                    Ralph Metzler <rjkm@metzlerbros.de>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License
118c2ecf20Sopenharmony_ci * version 2 only, as published by the Free Software Foundation.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful,
148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
168c2ecf20Sopenharmony_ci * GNU General Public License for more details.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "ddbridge.h"
208c2ecf20Sopenharmony_ci#include "ddbridge-io.h"
218c2ecf20Sopenharmony_ci#include "ddbridge-mci.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic const u32 MCLK = (1550000000 / 12);
248c2ecf20Sopenharmony_cistatic const u32 MAX_LDPC_BITRATE = (720000000);
258c2ecf20Sopenharmony_cistatic const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define SX8_TUNER_NUM 4
288c2ecf20Sopenharmony_ci#define SX8_DEMOD_NUM 8
298c2ecf20Sopenharmony_ci#define SX8_DEMOD_NONE 0xff
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct sx8_base {
328c2ecf20Sopenharmony_ci	struct mci_base      mci_base;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	u8                   tuner_use_count[SX8_TUNER_NUM];
358c2ecf20Sopenharmony_ci	u32                  gain_mode[SX8_TUNER_NUM];
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	u32                  used_ldpc_bitrate[SX8_DEMOD_NUM];
388c2ecf20Sopenharmony_ci	u8                   demod_in_use[SX8_DEMOD_NUM];
398c2ecf20Sopenharmony_ci	u32                  iq_mode;
408c2ecf20Sopenharmony_ci	u32                  burst_size;
418c2ecf20Sopenharmony_ci	u32                  direct_mode;
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct sx8 {
458c2ecf20Sopenharmony_ci	struct mci           mci;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	int                  first_time_lock;
488c2ecf20Sopenharmony_ci	int                  started;
498c2ecf20Sopenharmony_ci	struct mci_result    signal_info;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	u32                  bb_mode;
528c2ecf20Sopenharmony_ci	u32                  local_frequency;
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void release(struct dvb_frontend *fe)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
588c2ecf20Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	mci_base->count--;
618c2ecf20Sopenharmony_ci	if (mci_base->count == 0) {
628c2ecf20Sopenharmony_ci		list_del(&mci_base->mci_list);
638c2ecf20Sopenharmony_ci		kfree(mci_base);
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	kfree(state);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int get_info(struct dvb_frontend *fe)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int stat;
718c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
728c2ecf20Sopenharmony_ci	struct mci_command cmd;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
758c2ecf20Sopenharmony_ci	cmd.command = MCI_CMD_GETSIGNALINFO;
768c2ecf20Sopenharmony_ci	cmd.demod = state->mci.demod;
778c2ecf20Sopenharmony_ci	stat = ddb_mci_cmd(&state->mci, &cmd, &state->signal_info);
788c2ecf20Sopenharmony_ci	return stat;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int get_snr(struct dvb_frontend *fe)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
848c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	p->cnr.len = 1;
878c2ecf20Sopenharmony_ci	p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
888c2ecf20Sopenharmony_ci	p->cnr.stat[0].svalue =
898c2ecf20Sopenharmony_ci		(s64)state->signal_info.dvbs2_signal_info.signal_to_noise
908c2ecf20Sopenharmony_ci		     * 10;
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int get_strength(struct dvb_frontend *fe)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
978c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
988c2ecf20Sopenharmony_ci	s32 str;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	str = 100000 -
1018c2ecf20Sopenharmony_ci	      (state->signal_info.dvbs2_signal_info.channel_power
1028c2ecf20Sopenharmony_ci	       * 10 + 108750);
1038c2ecf20Sopenharmony_ci	p->strength.len = 1;
1048c2ecf20Sopenharmony_ci	p->strength.stat[0].scale = FE_SCALE_DECIBEL;
1058c2ecf20Sopenharmony_ci	p->strength.stat[0].svalue = str;
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int read_status(struct dvb_frontend *fe, enum fe_status *status)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int stat;
1128c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
1138c2ecf20Sopenharmony_ci	struct mci_command cmd;
1148c2ecf20Sopenharmony_ci	struct mci_result res;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	cmd.command = MCI_CMD_GETSTATUS;
1178c2ecf20Sopenharmony_ci	cmd.demod = state->mci.demod;
1188c2ecf20Sopenharmony_ci	stat = ddb_mci_cmd(&state->mci, &cmd, &res);
1198c2ecf20Sopenharmony_ci	if (stat)
1208c2ecf20Sopenharmony_ci		return stat;
1218c2ecf20Sopenharmony_ci	*status = 0x00;
1228c2ecf20Sopenharmony_ci	get_info(fe);
1238c2ecf20Sopenharmony_ci	get_strength(fe);
1248c2ecf20Sopenharmony_ci	if (res.status == SX8_DEMOD_WAIT_MATYPE)
1258c2ecf20Sopenharmony_ci		*status = 0x0f;
1268c2ecf20Sopenharmony_ci	if (res.status == SX8_DEMOD_LOCKED) {
1278c2ecf20Sopenharmony_ci		*status = 0x1f;
1288c2ecf20Sopenharmony_ci		get_snr(fe);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci	return stat;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
1368c2ecf20Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
1378c2ecf20Sopenharmony_ci	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;
1388c2ecf20Sopenharmony_ci	struct mci_command cmd;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
1418c2ecf20Sopenharmony_ci	cmd.tuner = state->mci.tuner;
1428c2ecf20Sopenharmony_ci	cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE;
1438c2ecf20Sopenharmony_ci	cmd.sx8_input_enable.flags = sx8_base->gain_mode[state->mci.tuner];
1448c2ecf20Sopenharmony_ci	return ddb_mci_cmd(&state->mci, &cmd, NULL);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int stop(struct dvb_frontend *fe)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
1508c2ecf20Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
1518c2ecf20Sopenharmony_ci	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;
1528c2ecf20Sopenharmony_ci	struct mci_command cmd;
1538c2ecf20Sopenharmony_ci	u32 input = state->mci.tuner;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
1568c2ecf20Sopenharmony_ci	if (state->mci.demod != SX8_DEMOD_NONE) {
1578c2ecf20Sopenharmony_ci		cmd.command = MCI_CMD_STOP;
1588c2ecf20Sopenharmony_ci		cmd.demod = state->mci.demod;
1598c2ecf20Sopenharmony_ci		ddb_mci_cmd(&state->mci, &cmd, NULL);
1608c2ecf20Sopenharmony_ci		if (sx8_base->iq_mode) {
1618c2ecf20Sopenharmony_ci			cmd.command = SX8_CMD_DISABLE_IQOUTPUT;
1628c2ecf20Sopenharmony_ci			cmd.demod = state->mci.demod;
1638c2ecf20Sopenharmony_ci			cmd.output = 0;
1648c2ecf20Sopenharmony_ci			ddb_mci_cmd(&state->mci, &cmd, NULL);
1658c2ecf20Sopenharmony_ci			ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL);
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	mutex_lock(&mci_base->tuner_lock);
1698c2ecf20Sopenharmony_ci	sx8_base->tuner_use_count[input]--;
1708c2ecf20Sopenharmony_ci	if (!sx8_base->tuner_use_count[input])
1718c2ecf20Sopenharmony_ci		mci_set_tuner(fe, input, 0);
1728c2ecf20Sopenharmony_ci	if (state->mci.demod < SX8_DEMOD_NUM) {
1738c2ecf20Sopenharmony_ci		sx8_base->demod_in_use[state->mci.demod] = 0;
1748c2ecf20Sopenharmony_ci		state->mci.demod = SX8_DEMOD_NONE;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci	sx8_base->used_ldpc_bitrate[state->mci.nr] = 0;
1778c2ecf20Sopenharmony_ci	sx8_base->iq_mode = 0;
1788c2ecf20Sopenharmony_ci	mutex_unlock(&mci_base->tuner_lock);
1798c2ecf20Sopenharmony_ci	state->started = 0;
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
1868c2ecf20Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
1878c2ecf20Sopenharmony_ci	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;
1888c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
1898c2ecf20Sopenharmony_ci	u32 used_ldpc_bitrate = 0, free_ldpc_bitrate;
1908c2ecf20Sopenharmony_ci	u32 used_demods = 0;
1918c2ecf20Sopenharmony_ci	struct mci_command cmd;
1928c2ecf20Sopenharmony_ci	u32 input = state->mci.tuner;
1938c2ecf20Sopenharmony_ci	u32 bits_per_symbol = 0;
1948c2ecf20Sopenharmony_ci	int i = -1, stat = 0;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (p->symbol_rate >= (MCLK / 2))
1978c2ecf20Sopenharmony_ci		flags &= ~1;
1988c2ecf20Sopenharmony_ci	if ((flags & 3) == 0)
1998c2ecf20Sopenharmony_ci		return -EINVAL;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (flags & 2) {
2028c2ecf20Sopenharmony_ci		u32 tmp = modmask;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		bits_per_symbol = 1;
2058c2ecf20Sopenharmony_ci		while (tmp & 1) {
2068c2ecf20Sopenharmony_ci			tmp >>= 1;
2078c2ecf20Sopenharmony_ci			bits_per_symbol++;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	mutex_lock(&mci_base->tuner_lock);
2128c2ecf20Sopenharmony_ci	if (sx8_base->iq_mode) {
2138c2ecf20Sopenharmony_ci		stat = -EBUSY;
2148c2ecf20Sopenharmony_ci		goto unlock;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (sx8_base->direct_mode) {
2188c2ecf20Sopenharmony_ci		if (p->symbol_rate >= MCLK / 2) {
2198c2ecf20Sopenharmony_ci			if (state->mci.nr < 4)
2208c2ecf20Sopenharmony_ci				i = state->mci.nr;
2218c2ecf20Sopenharmony_ci		} else {
2228c2ecf20Sopenharmony_ci			i = state->mci.nr;
2238c2ecf20Sopenharmony_ci		}
2248c2ecf20Sopenharmony_ci	} else {
2258c2ecf20Sopenharmony_ci		for (i = 0; i < SX8_DEMOD_NUM; i++) {
2268c2ecf20Sopenharmony_ci			used_ldpc_bitrate += sx8_base->used_ldpc_bitrate[i];
2278c2ecf20Sopenharmony_ci			if (sx8_base->demod_in_use[i])
2288c2ecf20Sopenharmony_ci				used_demods++;
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci		if (used_ldpc_bitrate >= MAX_LDPC_BITRATE ||
2318c2ecf20Sopenharmony_ci		    ((ts_config & SX8_TSCONFIG_MODE_MASK) >
2328c2ecf20Sopenharmony_ci		     SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) {
2338c2ecf20Sopenharmony_ci			stat = -EBUSY;
2348c2ecf20Sopenharmony_ci			goto unlock;
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci		free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate;
2378c2ecf20Sopenharmony_ci		if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE)
2388c2ecf20Sopenharmony_ci			free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate)
2418c2ecf20Sopenharmony_ci			bits_per_symbol--;
2428c2ecf20Sopenharmony_ci		if (bits_per_symbol < 2) {
2438c2ecf20Sopenharmony_ci			stat = -EBUSY;
2448c2ecf20Sopenharmony_ci			goto unlock;
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		modmask &= ((1 << (bits_per_symbol - 1)) - 1);
2488c2ecf20Sopenharmony_ci		if (((flags & 0x02) != 0) && modmask == 0) {
2498c2ecf20Sopenharmony_ci			stat = -EBUSY;
2508c2ecf20Sopenharmony_ci			goto unlock;
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7;
2548c2ecf20Sopenharmony_ci		while (i >= 0 && sx8_base->demod_in_use[i])
2558c2ecf20Sopenharmony_ci			i--;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (i < 0) {
2598c2ecf20Sopenharmony_ci		stat = -EBUSY;
2608c2ecf20Sopenharmony_ci		goto unlock;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci	sx8_base->demod_in_use[i] = 1;
2638c2ecf20Sopenharmony_ci	sx8_base->used_ldpc_bitrate[state->mci.nr] = p->symbol_rate
2648c2ecf20Sopenharmony_ci						     * bits_per_symbol;
2658c2ecf20Sopenharmony_ci	state->mci.demod = i;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (!sx8_base->tuner_use_count[input])
2688c2ecf20Sopenharmony_ci		mci_set_tuner(fe, input, 1);
2698c2ecf20Sopenharmony_ci	sx8_base->tuner_use_count[input]++;
2708c2ecf20Sopenharmony_ci	sx8_base->iq_mode = (ts_config > 1);
2718c2ecf20Sopenharmony_ciunlock:
2728c2ecf20Sopenharmony_ci	mutex_unlock(&mci_base->tuner_lock);
2738c2ecf20Sopenharmony_ci	if (stat)
2748c2ecf20Sopenharmony_ci		return stat;
2758c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (sx8_base->iq_mode) {
2788c2ecf20Sopenharmony_ci		cmd.command = SX8_CMD_ENABLE_IQOUTPUT;
2798c2ecf20Sopenharmony_ci		cmd.demod = state->mci.demod;
2808c2ecf20Sopenharmony_ci		cmd.output = 0;
2818c2ecf20Sopenharmony_ci		ddb_mci_cmd(&state->mci, &cmd, NULL);
2828c2ecf20Sopenharmony_ci		ddb_mci_config(&state->mci, ts_config);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci	if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000)
2858c2ecf20Sopenharmony_ci		flags |= 0x80;
2868c2ecf20Sopenharmony_ci	dev_dbg(mci_base->dev, "MCI-%d: tuner=%d demod=%d\n",
2878c2ecf20Sopenharmony_ci		state->mci.nr, state->mci.tuner, state->mci.demod);
2888c2ecf20Sopenharmony_ci	cmd.command = MCI_CMD_SEARCH_DVBS;
2898c2ecf20Sopenharmony_ci	cmd.dvbs2_search.flags = flags;
2908c2ecf20Sopenharmony_ci	cmd.dvbs2_search.s2_modulation_mask = modmask;
2918c2ecf20Sopenharmony_ci	cmd.dvbs2_search.retry = 2;
2928c2ecf20Sopenharmony_ci	cmd.dvbs2_search.frequency = p->frequency * 1000;
2938c2ecf20Sopenharmony_ci	cmd.dvbs2_search.symbol_rate = p->symbol_rate;
2948c2ecf20Sopenharmony_ci	cmd.dvbs2_search.scrambling_sequence_index =
2958c2ecf20Sopenharmony_ci		p->scrambling_sequence_index | 0x80000000;
2968c2ecf20Sopenharmony_ci	cmd.dvbs2_search.input_stream_id =
2978c2ecf20Sopenharmony_ci		(p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0;
2988c2ecf20Sopenharmony_ci	cmd.tuner = state->mci.tuner;
2998c2ecf20Sopenharmony_ci	cmd.demod = state->mci.demod;
3008c2ecf20Sopenharmony_ci	cmd.output = state->mci.nr;
3018c2ecf20Sopenharmony_ci	if (p->stream_id == 0x80000000)
3028c2ecf20Sopenharmony_ci		cmd.output |= 0x80;
3038c2ecf20Sopenharmony_ci	stat = ddb_mci_cmd(&state->mci, &cmd, NULL);
3048c2ecf20Sopenharmony_ci	if (stat)
3058c2ecf20Sopenharmony_ci		stop(fe);
3068c2ecf20Sopenharmony_ci	return stat;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int start_iq(struct dvb_frontend *fe, u32 flags, u32 roll_off,
3108c2ecf20Sopenharmony_ci		    u32 ts_config)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
3138c2ecf20Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
3148c2ecf20Sopenharmony_ci	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;
3158c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
3168c2ecf20Sopenharmony_ci	u32 used_demods = 0;
3178c2ecf20Sopenharmony_ci	struct mci_command cmd;
3188c2ecf20Sopenharmony_ci	u32 input = state->mci.tuner;
3198c2ecf20Sopenharmony_ci	int i, stat = 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	mutex_lock(&mci_base->tuner_lock);
3228c2ecf20Sopenharmony_ci	if (sx8_base->iq_mode) {
3238c2ecf20Sopenharmony_ci		stat = -EBUSY;
3248c2ecf20Sopenharmony_ci		goto unlock;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci	for (i = 0; i < SX8_DEMOD_NUM; i++)
3278c2ecf20Sopenharmony_ci		if (sx8_base->demod_in_use[i])
3288c2ecf20Sopenharmony_ci			used_demods++;
3298c2ecf20Sopenharmony_ci	if (used_demods > 0) {
3308c2ecf20Sopenharmony_ci		stat = -EBUSY;
3318c2ecf20Sopenharmony_ci		goto unlock;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci	state->mci.demod = 0;
3348c2ecf20Sopenharmony_ci	if (!sx8_base->tuner_use_count[input])
3358c2ecf20Sopenharmony_ci		mci_set_tuner(fe, input, 1);
3368c2ecf20Sopenharmony_ci	sx8_base->tuner_use_count[input]++;
3378c2ecf20Sopenharmony_ci	sx8_base->iq_mode = (ts_config > 1);
3388c2ecf20Sopenharmony_ciunlock:
3398c2ecf20Sopenharmony_ci	mutex_unlock(&mci_base->tuner_lock);
3408c2ecf20Sopenharmony_ci	if (stat)
3418c2ecf20Sopenharmony_ci		return stat;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
3448c2ecf20Sopenharmony_ci	cmd.command = SX8_CMD_START_IQ;
3458c2ecf20Sopenharmony_ci	cmd.sx8_start_iq.flags = flags;
3468c2ecf20Sopenharmony_ci	cmd.sx8_start_iq.roll_off = roll_off;
3478c2ecf20Sopenharmony_ci	cmd.sx8_start_iq.frequency = p->frequency * 1000;
3488c2ecf20Sopenharmony_ci	cmd.sx8_start_iq.symbol_rate = p->symbol_rate;
3498c2ecf20Sopenharmony_ci	cmd.tuner = state->mci.tuner;
3508c2ecf20Sopenharmony_ci	cmd.demod = state->mci.demod;
3518c2ecf20Sopenharmony_ci	stat = ddb_mci_cmd(&state->mci, &cmd, NULL);
3528c2ecf20Sopenharmony_ci	if (stat)
3538c2ecf20Sopenharmony_ci		stop(fe);
3548c2ecf20Sopenharmony_ci	ddb_mci_config(&state->mci, ts_config);
3558c2ecf20Sopenharmony_ci	return stat;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int set_parameters(struct dvb_frontend *fe)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	int stat = 0;
3618c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
3628c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
3638c2ecf20Sopenharmony_ci	u32 ts_config = SX8_TSCONFIG_MODE_NORMAL, iq_mode = 0, isi;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (state->started)
3668c2ecf20Sopenharmony_ci		stop(fe);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	isi = p->stream_id;
3698c2ecf20Sopenharmony_ci	if (isi != NO_STREAM_ID_FILTER)
3708c2ecf20Sopenharmony_ci		iq_mode = (isi & 0x30000000) >> 28;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (iq_mode)
3738c2ecf20Sopenharmony_ci		ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ);
3748c2ecf20Sopenharmony_ci	if (iq_mode < 3) {
3758c2ecf20Sopenharmony_ci		u32 mask;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		switch (p->modulation) {
3788c2ecf20Sopenharmony_ci		/* uncomment whenever these modulations hit the DVB API
3798c2ecf20Sopenharmony_ci		 *	case APSK_256:
3808c2ecf20Sopenharmony_ci		 *		mask = 0x7f;
3818c2ecf20Sopenharmony_ci		 *		break;
3828c2ecf20Sopenharmony_ci		 *	case APSK_128:
3838c2ecf20Sopenharmony_ci		 *		mask = 0x3f;
3848c2ecf20Sopenharmony_ci		 *		break;
3858c2ecf20Sopenharmony_ci		 *	case APSK_64:
3868c2ecf20Sopenharmony_ci		 *		mask = 0x1f;
3878c2ecf20Sopenharmony_ci		 *		break;
3888c2ecf20Sopenharmony_ci		 */
3898c2ecf20Sopenharmony_ci		case APSK_32:
3908c2ecf20Sopenharmony_ci			mask = 0x0f;
3918c2ecf20Sopenharmony_ci			break;
3928c2ecf20Sopenharmony_ci		case APSK_16:
3938c2ecf20Sopenharmony_ci			mask = 0x07;
3948c2ecf20Sopenharmony_ci			break;
3958c2ecf20Sopenharmony_ci		default:
3968c2ecf20Sopenharmony_ci			mask = 0x03;
3978c2ecf20Sopenharmony_ci			break;
3988c2ecf20Sopenharmony_ci		}
3998c2ecf20Sopenharmony_ci		stat = start(fe, 3, mask, ts_config);
4008c2ecf20Sopenharmony_ci	} else {
4018c2ecf20Sopenharmony_ci		stat = start_iq(fe, 0, 4, ts_config);
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci	if (!stat) {
4048c2ecf20Sopenharmony_ci		state->started = 1;
4058c2ecf20Sopenharmony_ci		state->first_time_lock = 1;
4068c2ecf20Sopenharmony_ci		state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return stat;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int tune(struct dvb_frontend *fe, bool re_tune,
4138c2ecf20Sopenharmony_ci		unsigned int mode_flags,
4148c2ecf20Sopenharmony_ci		unsigned int *delay, enum fe_status *status)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	int r;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (re_tune) {
4198c2ecf20Sopenharmony_ci		r = set_parameters(fe);
4208c2ecf20Sopenharmony_ci		if (r)
4218c2ecf20Sopenharmony_ci			return r;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci	r = read_status(fe, status);
4248c2ecf20Sopenharmony_ci	if (r)
4258c2ecf20Sopenharmony_ci		return r;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (*status & FE_HAS_LOCK)
4288c2ecf20Sopenharmony_ci		return 0;
4298c2ecf20Sopenharmony_ci	*delay = HZ / 10;
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic enum dvbfe_algo get_algo(struct dvb_frontend *fe)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	return DVBFE_ALGO_HW;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int set_input(struct dvb_frontend *fe, int input)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
4418c2ecf20Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (input >= SX8_TUNER_NUM)
4448c2ecf20Sopenharmony_ci		return -EINVAL;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	state->mci.tuner = input;
4478c2ecf20Sopenharmony_ci	dev_dbg(mci_base->dev, "MCI-%d: input=%d\n", state->mci.nr, input);
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic struct dvb_frontend_ops sx8_ops = {
4528c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBS, SYS_DVBS2 },
4538c2ecf20Sopenharmony_ci	.info = {
4548c2ecf20Sopenharmony_ci		.name			= "Digital Devices MaxSX8 MCI DVB-S/S2/S2X",
4558c2ecf20Sopenharmony_ci		.frequency_min_hz	=  950 * MHz,
4568c2ecf20Sopenharmony_ci		.frequency_max_hz	= 2150 * MHz,
4578c2ecf20Sopenharmony_ci		.symbol_rate_min	= 100000,
4588c2ecf20Sopenharmony_ci		.symbol_rate_max	= 100000000,
4598c2ecf20Sopenharmony_ci		.caps			= FE_CAN_INVERSION_AUTO |
4608c2ecf20Sopenharmony_ci					  FE_CAN_FEC_AUTO       |
4618c2ecf20Sopenharmony_ci					  FE_CAN_QPSK           |
4628c2ecf20Sopenharmony_ci					  FE_CAN_2G_MODULATION  |
4638c2ecf20Sopenharmony_ci					  FE_CAN_MULTISTREAM,
4648c2ecf20Sopenharmony_ci	},
4658c2ecf20Sopenharmony_ci	.get_frontend_algo		= get_algo,
4668c2ecf20Sopenharmony_ci	.tune				= tune,
4678c2ecf20Sopenharmony_ci	.release			= release,
4688c2ecf20Sopenharmony_ci	.read_status			= read_status,
4698c2ecf20Sopenharmony_ci};
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic int init(struct mci *mci)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	struct sx8 *state = (struct sx8 *)mci;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	state->mci.demod = SX8_DEMOD_NONE;
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ciconst struct mci_cfg ddb_max_sx8_cfg = {
4808c2ecf20Sopenharmony_ci	.type = 0,
4818c2ecf20Sopenharmony_ci	.fe_ops = &sx8_ops,
4828c2ecf20Sopenharmony_ci	.base_size = sizeof(struct sx8_base),
4838c2ecf20Sopenharmony_ci	.state_size = sizeof(struct sx8),
4848c2ecf20Sopenharmony_ci	.init = init,
4858c2ecf20Sopenharmony_ci	.set_input = set_input,
4868c2ecf20Sopenharmony_ci};
487