162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ddbridge-sx8.c: Digital Devices MAX SX8 driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018 Digital Devices GmbH
662306a36Sopenharmony_ci *                    Marcus Metzler <mocm@metzlerbros.de>
762306a36Sopenharmony_ci *                    Ralph Metzler <rjkm@metzlerbros.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "ddbridge.h"
1162306a36Sopenharmony_ci#include "ddbridge-io.h"
1262306a36Sopenharmony_ci#include "ddbridge-mci.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic const u32 MCLK = (1550000000 / 12);
1562306a36Sopenharmony_cistatic const u32 MAX_LDPC_BITRATE = (720000000);
1662306a36Sopenharmony_cistatic const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6);
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define SX8_TUNER_NUM 4
1962306a36Sopenharmony_ci#define SX8_DEMOD_NUM 8
2062306a36Sopenharmony_ci#define SX8_DEMOD_NONE 0xff
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct sx8_base {
2362306a36Sopenharmony_ci	struct mci_base      mci_base;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	u8                   tuner_use_count[SX8_TUNER_NUM];
2662306a36Sopenharmony_ci	u32                  gain_mode[SX8_TUNER_NUM];
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	u32                  used_ldpc_bitrate[SX8_DEMOD_NUM];
2962306a36Sopenharmony_ci	u8                   demod_in_use[SX8_DEMOD_NUM];
3062306a36Sopenharmony_ci	u32                  iq_mode;
3162306a36Sopenharmony_ci	u32                  burst_size;
3262306a36Sopenharmony_ci	u32                  direct_mode;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct sx8 {
3662306a36Sopenharmony_ci	struct mci           mci;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	int                  first_time_lock;
3962306a36Sopenharmony_ci	int                  started;
4062306a36Sopenharmony_ci	struct mci_result    signal_info;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	u32                  bb_mode;
4362306a36Sopenharmony_ci	u32                  local_frequency;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void release(struct dvb_frontend *fe)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
4962306a36Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	mci_base->count--;
5262306a36Sopenharmony_ci	if (mci_base->count == 0) {
5362306a36Sopenharmony_ci		list_del(&mci_base->mci_list);
5462306a36Sopenharmony_ci		kfree(mci_base);
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	kfree(state);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int get_info(struct dvb_frontend *fe)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	int stat;
6262306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
6362306a36Sopenharmony_ci	struct mci_command cmd;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
6662306a36Sopenharmony_ci	cmd.command = MCI_CMD_GETSIGNALINFO;
6762306a36Sopenharmony_ci	cmd.demod = state->mci.demod;
6862306a36Sopenharmony_ci	stat = ddb_mci_cmd(&state->mci, &cmd, &state->signal_info);
6962306a36Sopenharmony_ci	return stat;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int get_snr(struct dvb_frontend *fe)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
7562306a36Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	p->cnr.len = 1;
7862306a36Sopenharmony_ci	p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
7962306a36Sopenharmony_ci	p->cnr.stat[0].svalue =
8062306a36Sopenharmony_ci		(s64)state->signal_info.dvbs2_signal_info.signal_to_noise
8162306a36Sopenharmony_ci		     * 10;
8262306a36Sopenharmony_ci	return 0;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int get_strength(struct dvb_frontend *fe)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
8862306a36Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
8962306a36Sopenharmony_ci	s32 str;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	str = 100000 -
9262306a36Sopenharmony_ci	      (state->signal_info.dvbs2_signal_info.channel_power
9362306a36Sopenharmony_ci	       * 10 + 108750);
9462306a36Sopenharmony_ci	p->strength.len = 1;
9562306a36Sopenharmony_ci	p->strength.stat[0].scale = FE_SCALE_DECIBEL;
9662306a36Sopenharmony_ci	p->strength.stat[0].svalue = str;
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int read_status(struct dvb_frontend *fe, enum fe_status *status)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	int stat;
10362306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
10462306a36Sopenharmony_ci	struct mci_command cmd;
10562306a36Sopenharmony_ci	struct mci_result res;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	cmd.command = MCI_CMD_GETSTATUS;
10862306a36Sopenharmony_ci	cmd.demod = state->mci.demod;
10962306a36Sopenharmony_ci	stat = ddb_mci_cmd(&state->mci, &cmd, &res);
11062306a36Sopenharmony_ci	if (stat)
11162306a36Sopenharmony_ci		return stat;
11262306a36Sopenharmony_ci	*status = 0x00;
11362306a36Sopenharmony_ci	get_info(fe);
11462306a36Sopenharmony_ci	get_strength(fe);
11562306a36Sopenharmony_ci	if (res.status == SX8_DEMOD_WAIT_MATYPE)
11662306a36Sopenharmony_ci		*status = 0x0f;
11762306a36Sopenharmony_ci	if (res.status == SX8_DEMOD_LOCKED) {
11862306a36Sopenharmony_ci		*status = 0x1f;
11962306a36Sopenharmony_ci		get_snr(fe);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	return stat;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
12762306a36Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
12862306a36Sopenharmony_ci	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;
12962306a36Sopenharmony_ci	struct mci_command cmd;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
13262306a36Sopenharmony_ci	cmd.tuner = state->mci.tuner;
13362306a36Sopenharmony_ci	cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE;
13462306a36Sopenharmony_ci	cmd.sx8_input_enable.flags = sx8_base->gain_mode[state->mci.tuner];
13562306a36Sopenharmony_ci	return ddb_mci_cmd(&state->mci, &cmd, NULL);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int stop(struct dvb_frontend *fe)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
14162306a36Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
14262306a36Sopenharmony_ci	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;
14362306a36Sopenharmony_ci	struct mci_command cmd;
14462306a36Sopenharmony_ci	u32 input = state->mci.tuner;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
14762306a36Sopenharmony_ci	if (state->mci.demod != SX8_DEMOD_NONE) {
14862306a36Sopenharmony_ci		cmd.command = MCI_CMD_STOP;
14962306a36Sopenharmony_ci		cmd.demod = state->mci.demod;
15062306a36Sopenharmony_ci		ddb_mci_cmd(&state->mci, &cmd, NULL);
15162306a36Sopenharmony_ci		if (sx8_base->iq_mode) {
15262306a36Sopenharmony_ci			cmd.command = SX8_CMD_DISABLE_IQOUTPUT;
15362306a36Sopenharmony_ci			cmd.demod = state->mci.demod;
15462306a36Sopenharmony_ci			cmd.output = 0;
15562306a36Sopenharmony_ci			ddb_mci_cmd(&state->mci, &cmd, NULL);
15662306a36Sopenharmony_ci			ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL);
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci	mutex_lock(&mci_base->tuner_lock);
16062306a36Sopenharmony_ci	sx8_base->tuner_use_count[input]--;
16162306a36Sopenharmony_ci	if (!sx8_base->tuner_use_count[input])
16262306a36Sopenharmony_ci		mci_set_tuner(fe, input, 0);
16362306a36Sopenharmony_ci	if (state->mci.demod < SX8_DEMOD_NUM) {
16462306a36Sopenharmony_ci		sx8_base->demod_in_use[state->mci.demod] = 0;
16562306a36Sopenharmony_ci		state->mci.demod = SX8_DEMOD_NONE;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci	sx8_base->used_ldpc_bitrate[state->mci.nr] = 0;
16862306a36Sopenharmony_ci	sx8_base->iq_mode = 0;
16962306a36Sopenharmony_ci	mutex_unlock(&mci_base->tuner_lock);
17062306a36Sopenharmony_ci	state->started = 0;
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
17762306a36Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
17862306a36Sopenharmony_ci	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;
17962306a36Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
18062306a36Sopenharmony_ci	u32 used_ldpc_bitrate = 0, free_ldpc_bitrate;
18162306a36Sopenharmony_ci	u32 used_demods = 0;
18262306a36Sopenharmony_ci	struct mci_command cmd;
18362306a36Sopenharmony_ci	u32 input = state->mci.tuner;
18462306a36Sopenharmony_ci	u32 bits_per_symbol = 0;
18562306a36Sopenharmony_ci	int i = -1, stat = 0;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (p->symbol_rate >= (MCLK / 2))
18862306a36Sopenharmony_ci		flags &= ~1;
18962306a36Sopenharmony_ci	if ((flags & 3) == 0)
19062306a36Sopenharmony_ci		return -EINVAL;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (flags & 2) {
19362306a36Sopenharmony_ci		u32 tmp = modmask;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		bits_per_symbol = 1;
19662306a36Sopenharmony_ci		while (tmp & 1) {
19762306a36Sopenharmony_ci			tmp >>= 1;
19862306a36Sopenharmony_ci			bits_per_symbol++;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	mutex_lock(&mci_base->tuner_lock);
20362306a36Sopenharmony_ci	if (sx8_base->iq_mode) {
20462306a36Sopenharmony_ci		stat = -EBUSY;
20562306a36Sopenharmony_ci		goto unlock;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (sx8_base->direct_mode) {
20962306a36Sopenharmony_ci		if (p->symbol_rate >= MCLK / 2) {
21062306a36Sopenharmony_ci			if (state->mci.nr < 4)
21162306a36Sopenharmony_ci				i = state->mci.nr;
21262306a36Sopenharmony_ci		} else {
21362306a36Sopenharmony_ci			i = state->mci.nr;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci	} else {
21662306a36Sopenharmony_ci		for (i = 0; i < SX8_DEMOD_NUM; i++) {
21762306a36Sopenharmony_ci			used_ldpc_bitrate += sx8_base->used_ldpc_bitrate[i];
21862306a36Sopenharmony_ci			if (sx8_base->demod_in_use[i])
21962306a36Sopenharmony_ci				used_demods++;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci		if (used_ldpc_bitrate >= MAX_LDPC_BITRATE ||
22262306a36Sopenharmony_ci		    ((ts_config & SX8_TSCONFIG_MODE_MASK) >
22362306a36Sopenharmony_ci		     SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) {
22462306a36Sopenharmony_ci			stat = -EBUSY;
22562306a36Sopenharmony_ci			goto unlock;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci		free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate;
22862306a36Sopenharmony_ci		if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE)
22962306a36Sopenharmony_ci			free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate)
23262306a36Sopenharmony_ci			bits_per_symbol--;
23362306a36Sopenharmony_ci		if (bits_per_symbol < 2) {
23462306a36Sopenharmony_ci			stat = -EBUSY;
23562306a36Sopenharmony_ci			goto unlock;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		modmask &= ((1 << (bits_per_symbol - 1)) - 1);
23962306a36Sopenharmony_ci		if (((flags & 0x02) != 0) && modmask == 0) {
24062306a36Sopenharmony_ci			stat = -EBUSY;
24162306a36Sopenharmony_ci			goto unlock;
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7;
24562306a36Sopenharmony_ci		while (i >= 0 && sx8_base->demod_in_use[i])
24662306a36Sopenharmony_ci			i--;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (i < 0) {
25062306a36Sopenharmony_ci		stat = -EBUSY;
25162306a36Sopenharmony_ci		goto unlock;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	sx8_base->demod_in_use[i] = 1;
25462306a36Sopenharmony_ci	sx8_base->used_ldpc_bitrate[state->mci.nr] = p->symbol_rate
25562306a36Sopenharmony_ci						     * bits_per_symbol;
25662306a36Sopenharmony_ci	state->mci.demod = i;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!sx8_base->tuner_use_count[input])
25962306a36Sopenharmony_ci		mci_set_tuner(fe, input, 1);
26062306a36Sopenharmony_ci	sx8_base->tuner_use_count[input]++;
26162306a36Sopenharmony_ci	sx8_base->iq_mode = (ts_config > 1);
26262306a36Sopenharmony_ciunlock:
26362306a36Sopenharmony_ci	mutex_unlock(&mci_base->tuner_lock);
26462306a36Sopenharmony_ci	if (stat)
26562306a36Sopenharmony_ci		return stat;
26662306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (sx8_base->iq_mode) {
26962306a36Sopenharmony_ci		cmd.command = SX8_CMD_ENABLE_IQOUTPUT;
27062306a36Sopenharmony_ci		cmd.demod = state->mci.demod;
27162306a36Sopenharmony_ci		cmd.output = 0;
27262306a36Sopenharmony_ci		ddb_mci_cmd(&state->mci, &cmd, NULL);
27362306a36Sopenharmony_ci		ddb_mci_config(&state->mci, ts_config);
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci	if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000)
27662306a36Sopenharmony_ci		flags |= 0x80;
27762306a36Sopenharmony_ci	dev_dbg(mci_base->dev, "MCI-%d: tuner=%d demod=%d\n",
27862306a36Sopenharmony_ci		state->mci.nr, state->mci.tuner, state->mci.demod);
27962306a36Sopenharmony_ci	cmd.command = MCI_CMD_SEARCH_DVBS;
28062306a36Sopenharmony_ci	cmd.dvbs2_search.flags = flags;
28162306a36Sopenharmony_ci	cmd.dvbs2_search.s2_modulation_mask = modmask;
28262306a36Sopenharmony_ci	cmd.dvbs2_search.retry = 2;
28362306a36Sopenharmony_ci	cmd.dvbs2_search.frequency = p->frequency * 1000;
28462306a36Sopenharmony_ci	cmd.dvbs2_search.symbol_rate = p->symbol_rate;
28562306a36Sopenharmony_ci	cmd.dvbs2_search.scrambling_sequence_index =
28662306a36Sopenharmony_ci		p->scrambling_sequence_index | 0x80000000;
28762306a36Sopenharmony_ci	cmd.dvbs2_search.input_stream_id =
28862306a36Sopenharmony_ci		(p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0;
28962306a36Sopenharmony_ci	cmd.tuner = state->mci.tuner;
29062306a36Sopenharmony_ci	cmd.demod = state->mci.demod;
29162306a36Sopenharmony_ci	cmd.output = state->mci.nr;
29262306a36Sopenharmony_ci	if (p->stream_id == 0x80000000)
29362306a36Sopenharmony_ci		cmd.output |= 0x80;
29462306a36Sopenharmony_ci	stat = ddb_mci_cmd(&state->mci, &cmd, NULL);
29562306a36Sopenharmony_ci	if (stat)
29662306a36Sopenharmony_ci		stop(fe);
29762306a36Sopenharmony_ci	return stat;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int start_iq(struct dvb_frontend *fe, u32 flags, u32 roll_off,
30162306a36Sopenharmony_ci		    u32 ts_config)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
30462306a36Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
30562306a36Sopenharmony_ci	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;
30662306a36Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
30762306a36Sopenharmony_ci	u32 used_demods = 0;
30862306a36Sopenharmony_ci	struct mci_command cmd;
30962306a36Sopenharmony_ci	u32 input = state->mci.tuner;
31062306a36Sopenharmony_ci	int i, stat = 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	mutex_lock(&mci_base->tuner_lock);
31362306a36Sopenharmony_ci	if (sx8_base->iq_mode) {
31462306a36Sopenharmony_ci		stat = -EBUSY;
31562306a36Sopenharmony_ci		goto unlock;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci	for (i = 0; i < SX8_DEMOD_NUM; i++)
31862306a36Sopenharmony_ci		if (sx8_base->demod_in_use[i])
31962306a36Sopenharmony_ci			used_demods++;
32062306a36Sopenharmony_ci	if (used_demods > 0) {
32162306a36Sopenharmony_ci		stat = -EBUSY;
32262306a36Sopenharmony_ci		goto unlock;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci	state->mci.demod = 0;
32562306a36Sopenharmony_ci	if (!sx8_base->tuner_use_count[input])
32662306a36Sopenharmony_ci		mci_set_tuner(fe, input, 1);
32762306a36Sopenharmony_ci	sx8_base->tuner_use_count[input]++;
32862306a36Sopenharmony_ci	sx8_base->iq_mode = (ts_config > 1);
32962306a36Sopenharmony_ciunlock:
33062306a36Sopenharmony_ci	mutex_unlock(&mci_base->tuner_lock);
33162306a36Sopenharmony_ci	if (stat)
33262306a36Sopenharmony_ci		return stat;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
33562306a36Sopenharmony_ci	cmd.command = SX8_CMD_START_IQ;
33662306a36Sopenharmony_ci	cmd.sx8_start_iq.flags = flags;
33762306a36Sopenharmony_ci	cmd.sx8_start_iq.roll_off = roll_off;
33862306a36Sopenharmony_ci	cmd.sx8_start_iq.frequency = p->frequency * 1000;
33962306a36Sopenharmony_ci	cmd.sx8_start_iq.symbol_rate = p->symbol_rate;
34062306a36Sopenharmony_ci	cmd.tuner = state->mci.tuner;
34162306a36Sopenharmony_ci	cmd.demod = state->mci.demod;
34262306a36Sopenharmony_ci	stat = ddb_mci_cmd(&state->mci, &cmd, NULL);
34362306a36Sopenharmony_ci	if (stat)
34462306a36Sopenharmony_ci		stop(fe);
34562306a36Sopenharmony_ci	ddb_mci_config(&state->mci, ts_config);
34662306a36Sopenharmony_ci	return stat;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int set_parameters(struct dvb_frontend *fe)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	int stat = 0;
35262306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
35362306a36Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
35462306a36Sopenharmony_ci	u32 ts_config = SX8_TSCONFIG_MODE_NORMAL, iq_mode = 0, isi;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (state->started)
35762306a36Sopenharmony_ci		stop(fe);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	isi = p->stream_id;
36062306a36Sopenharmony_ci	if (isi != NO_STREAM_ID_FILTER)
36162306a36Sopenharmony_ci		iq_mode = (isi & 0x30000000) >> 28;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (iq_mode)
36462306a36Sopenharmony_ci		ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ);
36562306a36Sopenharmony_ci	if (iq_mode < 3) {
36662306a36Sopenharmony_ci		u32 mask;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		switch (p->modulation) {
36962306a36Sopenharmony_ci		/* uncomment whenever these modulations hit the DVB API
37062306a36Sopenharmony_ci		 *	case APSK_256:
37162306a36Sopenharmony_ci		 *		mask = 0x7f;
37262306a36Sopenharmony_ci		 *		break;
37362306a36Sopenharmony_ci		 *	case APSK_128:
37462306a36Sopenharmony_ci		 *		mask = 0x3f;
37562306a36Sopenharmony_ci		 *		break;
37662306a36Sopenharmony_ci		 *	case APSK_64:
37762306a36Sopenharmony_ci		 *		mask = 0x1f;
37862306a36Sopenharmony_ci		 *		break;
37962306a36Sopenharmony_ci		 */
38062306a36Sopenharmony_ci		case APSK_32:
38162306a36Sopenharmony_ci			mask = 0x0f;
38262306a36Sopenharmony_ci			break;
38362306a36Sopenharmony_ci		case APSK_16:
38462306a36Sopenharmony_ci			mask = 0x07;
38562306a36Sopenharmony_ci			break;
38662306a36Sopenharmony_ci		default:
38762306a36Sopenharmony_ci			mask = 0x03;
38862306a36Sopenharmony_ci			break;
38962306a36Sopenharmony_ci		}
39062306a36Sopenharmony_ci		stat = start(fe, 3, mask, ts_config);
39162306a36Sopenharmony_ci	} else {
39262306a36Sopenharmony_ci		stat = start_iq(fe, 0, 4, ts_config);
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	if (!stat) {
39562306a36Sopenharmony_ci		state->started = 1;
39662306a36Sopenharmony_ci		state->first_time_lock = 1;
39762306a36Sopenharmony_ci		state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return stat;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int tune(struct dvb_frontend *fe, bool re_tune,
40462306a36Sopenharmony_ci		unsigned int mode_flags,
40562306a36Sopenharmony_ci		unsigned int *delay, enum fe_status *status)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	int r;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (re_tune) {
41062306a36Sopenharmony_ci		r = set_parameters(fe);
41162306a36Sopenharmony_ci		if (r)
41262306a36Sopenharmony_ci			return r;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci	r = read_status(fe, status);
41562306a36Sopenharmony_ci	if (r)
41662306a36Sopenharmony_ci		return r;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (*status & FE_HAS_LOCK)
41962306a36Sopenharmony_ci		return 0;
42062306a36Sopenharmony_ci	*delay = HZ / 10;
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic enum dvbfe_algo get_algo(struct dvb_frontend *fe)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	return DVBFE_ALGO_HW;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic int set_input(struct dvb_frontend *fe, int input)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct sx8 *state = fe->demodulator_priv;
43262306a36Sopenharmony_ci	struct mci_base *mci_base = state->mci.base;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (input >= SX8_TUNER_NUM)
43562306a36Sopenharmony_ci		return -EINVAL;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	state->mci.tuner = input;
43862306a36Sopenharmony_ci	dev_dbg(mci_base->dev, "MCI-%d: input=%d\n", state->mci.nr, input);
43962306a36Sopenharmony_ci	return 0;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic struct dvb_frontend_ops sx8_ops = {
44362306a36Sopenharmony_ci	.delsys = { SYS_DVBS, SYS_DVBS2 },
44462306a36Sopenharmony_ci	.info = {
44562306a36Sopenharmony_ci		.name			= "Digital Devices MaxSX8 MCI DVB-S/S2/S2X",
44662306a36Sopenharmony_ci		.frequency_min_hz	=  950 * MHz,
44762306a36Sopenharmony_ci		.frequency_max_hz	= 2150 * MHz,
44862306a36Sopenharmony_ci		.symbol_rate_min	= 100000,
44962306a36Sopenharmony_ci		.symbol_rate_max	= 100000000,
45062306a36Sopenharmony_ci		.caps			= FE_CAN_INVERSION_AUTO |
45162306a36Sopenharmony_ci					  FE_CAN_FEC_AUTO       |
45262306a36Sopenharmony_ci					  FE_CAN_QPSK           |
45362306a36Sopenharmony_ci					  FE_CAN_2G_MODULATION  |
45462306a36Sopenharmony_ci					  FE_CAN_MULTISTREAM,
45562306a36Sopenharmony_ci	},
45662306a36Sopenharmony_ci	.get_frontend_algo		= get_algo,
45762306a36Sopenharmony_ci	.tune				= tune,
45862306a36Sopenharmony_ci	.release			= release,
45962306a36Sopenharmony_ci	.read_status			= read_status,
46062306a36Sopenharmony_ci};
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int init(struct mci *mci)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct sx8 *state = (struct sx8 *)mci;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	state->mci.demod = SX8_DEMOD_NONE;
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ciconst struct mci_cfg ddb_max_sx8_cfg = {
47162306a36Sopenharmony_ci	.type = 0,
47262306a36Sopenharmony_ci	.fe_ops = &sx8_ops,
47362306a36Sopenharmony_ci	.base_size = sizeof(struct sx8_base),
47462306a36Sopenharmony_ci	.state_size = sizeof(struct sx8),
47562306a36Sopenharmony_ci	.init = init,
47662306a36Sopenharmony_ci	.set_input = set_input,
47762306a36Sopenharmony_ci};
478