brintos

brintos / linux-shallow public Read only

0
0
Text · 11.5 KiB · c8de8d2 Raw
478 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ddbridge-sx8.c: Digital Devices MAX SX8 driver4 *5 * Copyright (C) 2018 Digital Devices GmbH6 *                    Marcus Metzler <mocm@metzlerbros.de>7 *                    Ralph Metzler <rjkm@metzlerbros.de>8 */9 10#include "ddbridge.h"11#include "ddbridge-io.h"12#include "ddbridge-mci.h"13 14static const u32 MCLK = (1550000000 / 12);15static const u32 MAX_LDPC_BITRATE = (720000000);16static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6);17 18#define SX8_TUNER_NUM 419#define SX8_DEMOD_NUM 820#define SX8_DEMOD_NONE 0xff21 22struct sx8_base {23	struct mci_base      mci_base;24 25	u8                   tuner_use_count[SX8_TUNER_NUM];26	u32                  gain_mode[SX8_TUNER_NUM];27 28	u32                  used_ldpc_bitrate[SX8_DEMOD_NUM];29	u8                   demod_in_use[SX8_DEMOD_NUM];30	u32                  iq_mode;31	u32                  burst_size;32	u32                  direct_mode;33};34 35struct sx8 {36	struct mci           mci;37 38	int                  first_time_lock;39	int                  started;40	struct mci_result    signal_info;41 42	u32                  bb_mode;43	u32                  local_frequency;44};45 46static void release(struct dvb_frontend *fe)47{48	struct sx8 *state = fe->demodulator_priv;49	struct mci_base *mci_base = state->mci.base;50 51	mci_base->count--;52	if (mci_base->count == 0) {53		list_del(&mci_base->mci_list);54		kfree(mci_base);55	}56	kfree(state);57}58 59static int get_info(struct dvb_frontend *fe)60{61	int stat;62	struct sx8 *state = fe->demodulator_priv;63	struct mci_command cmd;64 65	memset(&cmd, 0, sizeof(cmd));66	cmd.command = MCI_CMD_GETSIGNALINFO;67	cmd.demod = state->mci.demod;68	stat = ddb_mci_cmd(&state->mci, &cmd, &state->signal_info);69	return stat;70}71 72static int get_snr(struct dvb_frontend *fe)73{74	struct sx8 *state = fe->demodulator_priv;75	struct dtv_frontend_properties *p = &fe->dtv_property_cache;76 77	p->cnr.len = 1;78	p->cnr.stat[0].scale = FE_SCALE_DECIBEL;79	p->cnr.stat[0].svalue =80		(s64)state->signal_info.dvbs2_signal_info.signal_to_noise81		     * 10;82	return 0;83}84 85static int get_strength(struct dvb_frontend *fe)86{87	struct sx8 *state = fe->demodulator_priv;88	struct dtv_frontend_properties *p = &fe->dtv_property_cache;89	s32 str;90 91	str = 100000 -92	      (state->signal_info.dvbs2_signal_info.channel_power93	       * 10 + 108750);94	p->strength.len = 1;95	p->strength.stat[0].scale = FE_SCALE_DECIBEL;96	p->strength.stat[0].svalue = str;97	return 0;98}99 100static int read_status(struct dvb_frontend *fe, enum fe_status *status)101{102	int stat;103	struct sx8 *state = fe->demodulator_priv;104	struct mci_command cmd;105	struct mci_result res;106 107	cmd.command = MCI_CMD_GETSTATUS;108	cmd.demod = state->mci.demod;109	stat = ddb_mci_cmd(&state->mci, &cmd, &res);110	if (stat)111		return stat;112	*status = 0x00;113	get_info(fe);114	get_strength(fe);115	if (res.status == SX8_DEMOD_WAIT_MATYPE)116		*status = 0x0f;117	if (res.status == SX8_DEMOD_LOCKED) {118		*status = 0x1f;119		get_snr(fe);120	}121	return stat;122}123 124static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on)125{126	struct sx8 *state = fe->demodulator_priv;127	struct mci_base *mci_base = state->mci.base;128	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;129	struct mci_command cmd;130 131	memset(&cmd, 0, sizeof(cmd));132	cmd.tuner = state->mci.tuner;133	cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE;134	cmd.sx8_input_enable.flags = sx8_base->gain_mode[state->mci.tuner];135	return ddb_mci_cmd(&state->mci, &cmd, NULL);136}137 138static int stop(struct dvb_frontend *fe)139{140	struct sx8 *state = fe->demodulator_priv;141	struct mci_base *mci_base = state->mci.base;142	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;143	struct mci_command cmd;144	u32 input = state->mci.tuner;145 146	memset(&cmd, 0, sizeof(cmd));147	if (state->mci.demod != SX8_DEMOD_NONE) {148		cmd.command = MCI_CMD_STOP;149		cmd.demod = state->mci.demod;150		ddb_mci_cmd(&state->mci, &cmd, NULL);151		if (sx8_base->iq_mode) {152			cmd.command = SX8_CMD_DISABLE_IQOUTPUT;153			cmd.demod = state->mci.demod;154			cmd.output = 0;155			ddb_mci_cmd(&state->mci, &cmd, NULL);156			ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL);157		}158	}159	mutex_lock(&mci_base->tuner_lock);160	sx8_base->tuner_use_count[input]--;161	if (!sx8_base->tuner_use_count[input])162		mci_set_tuner(fe, input, 0);163	if (state->mci.demod < SX8_DEMOD_NUM) {164		sx8_base->demod_in_use[state->mci.demod] = 0;165		state->mci.demod = SX8_DEMOD_NONE;166	}167	sx8_base->used_ldpc_bitrate[state->mci.nr] = 0;168	sx8_base->iq_mode = 0;169	mutex_unlock(&mci_base->tuner_lock);170	state->started = 0;171	return 0;172}173 174static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config)175{176	struct sx8 *state = fe->demodulator_priv;177	struct mci_base *mci_base = state->mci.base;178	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;179	struct dtv_frontend_properties *p = &fe->dtv_property_cache;180	u32 used_ldpc_bitrate = 0, free_ldpc_bitrate;181	u32 used_demods = 0;182	struct mci_command cmd;183	u32 input = state->mci.tuner;184	u32 bits_per_symbol = 0;185	int i = -1, stat = 0;186 187	if (p->symbol_rate >= (MCLK / 2))188		flags &= ~1;189	if ((flags & 3) == 0)190		return -EINVAL;191 192	if (flags & 2) {193		u32 tmp = modmask;194 195		bits_per_symbol = 1;196		while (tmp & 1) {197			tmp >>= 1;198			bits_per_symbol++;199		}200	}201 202	mutex_lock(&mci_base->tuner_lock);203	if (sx8_base->iq_mode) {204		stat = -EBUSY;205		goto unlock;206	}207 208	if (sx8_base->direct_mode) {209		if (p->symbol_rate >= MCLK / 2) {210			if (state->mci.nr < 4)211				i = state->mci.nr;212		} else {213			i = state->mci.nr;214		}215	} else {216		for (i = 0; i < SX8_DEMOD_NUM; i++) {217			used_ldpc_bitrate += sx8_base->used_ldpc_bitrate[i];218			if (sx8_base->demod_in_use[i])219				used_demods++;220		}221		if (used_ldpc_bitrate >= MAX_LDPC_BITRATE ||222		    ((ts_config & SX8_TSCONFIG_MODE_MASK) >223		     SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) {224			stat = -EBUSY;225			goto unlock;226		}227		free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate;228		if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE)229			free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE;230 231		while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate)232			bits_per_symbol--;233		if (bits_per_symbol < 2) {234			stat = -EBUSY;235			goto unlock;236		}237 238		modmask &= ((1 << (bits_per_symbol - 1)) - 1);239		if (((flags & 0x02) != 0) && modmask == 0) {240			stat = -EBUSY;241			goto unlock;242		}243 244		i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7;245		while (i >= 0 && sx8_base->demod_in_use[i])246			i--;247	}248 249	if (i < 0) {250		stat = -EBUSY;251		goto unlock;252	}253	sx8_base->demod_in_use[i] = 1;254	sx8_base->used_ldpc_bitrate[state->mci.nr] = p->symbol_rate255						     * bits_per_symbol;256	state->mci.demod = i;257 258	if (!sx8_base->tuner_use_count[input])259		mci_set_tuner(fe, input, 1);260	sx8_base->tuner_use_count[input]++;261	sx8_base->iq_mode = (ts_config > 1);262unlock:263	mutex_unlock(&mci_base->tuner_lock);264	if (stat)265		return stat;266	memset(&cmd, 0, sizeof(cmd));267 268	if (sx8_base->iq_mode) {269		cmd.command = SX8_CMD_ENABLE_IQOUTPUT;270		cmd.demod = state->mci.demod;271		cmd.output = 0;272		ddb_mci_cmd(&state->mci, &cmd, NULL);273		ddb_mci_config(&state->mci, ts_config);274	}275	if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000)276		flags |= 0x80;277	dev_dbg(mci_base->dev, "MCI-%d: tuner=%d demod=%d\n",278		state->mci.nr, state->mci.tuner, state->mci.demod);279	cmd.command = MCI_CMD_SEARCH_DVBS;280	cmd.dvbs2_search.flags = flags;281	cmd.dvbs2_search.s2_modulation_mask = modmask;282	cmd.dvbs2_search.retry = 2;283	cmd.dvbs2_search.frequency = p->frequency * 1000;284	cmd.dvbs2_search.symbol_rate = p->symbol_rate;285	cmd.dvbs2_search.scrambling_sequence_index =286		p->scrambling_sequence_index | 0x80000000;287	cmd.dvbs2_search.input_stream_id =288		(p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0;289	cmd.tuner = state->mci.tuner;290	cmd.demod = state->mci.demod;291	cmd.output = state->mci.nr;292	if (p->stream_id == 0x80000000)293		cmd.output |= 0x80;294	stat = ddb_mci_cmd(&state->mci, &cmd, NULL);295	if (stat)296		stop(fe);297	return stat;298}299 300static int start_iq(struct dvb_frontend *fe, u32 flags, u32 roll_off,301		    u32 ts_config)302{303	struct sx8 *state = fe->demodulator_priv;304	struct mci_base *mci_base = state->mci.base;305	struct sx8_base *sx8_base = (struct sx8_base *)mci_base;306	struct dtv_frontend_properties *p = &fe->dtv_property_cache;307	u32 used_demods = 0;308	struct mci_command cmd;309	u32 input = state->mci.tuner;310	int i, stat = 0;311 312	mutex_lock(&mci_base->tuner_lock);313	if (sx8_base->iq_mode) {314		stat = -EBUSY;315		goto unlock;316	}317	for (i = 0; i < SX8_DEMOD_NUM; i++)318		if (sx8_base->demod_in_use[i])319			used_demods++;320	if (used_demods > 0) {321		stat = -EBUSY;322		goto unlock;323	}324	state->mci.demod = 0;325	if (!sx8_base->tuner_use_count[input])326		mci_set_tuner(fe, input, 1);327	sx8_base->tuner_use_count[input]++;328	sx8_base->iq_mode = (ts_config > 1);329unlock:330	mutex_unlock(&mci_base->tuner_lock);331	if (stat)332		return stat;333 334	memset(&cmd, 0, sizeof(cmd));335	cmd.command = SX8_CMD_START_IQ;336	cmd.sx8_start_iq.flags = flags;337	cmd.sx8_start_iq.roll_off = roll_off;338	cmd.sx8_start_iq.frequency = p->frequency * 1000;339	cmd.sx8_start_iq.symbol_rate = p->symbol_rate;340	cmd.tuner = state->mci.tuner;341	cmd.demod = state->mci.demod;342	stat = ddb_mci_cmd(&state->mci, &cmd, NULL);343	if (stat)344		stop(fe);345	ddb_mci_config(&state->mci, ts_config);346	return stat;347}348 349static int set_parameters(struct dvb_frontend *fe)350{351	int stat = 0;352	struct sx8 *state = fe->demodulator_priv;353	struct dtv_frontend_properties *p = &fe->dtv_property_cache;354	u32 ts_config = SX8_TSCONFIG_MODE_NORMAL, iq_mode = 0, isi;355 356	if (state->started)357		stop(fe);358 359	isi = p->stream_id;360	if (isi != NO_STREAM_ID_FILTER)361		iq_mode = (isi & 0x30000000) >> 28;362 363	if (iq_mode)364		ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ);365	if (iq_mode < 3) {366		u32 mask;367 368		switch (p->modulation) {369		/* uncomment whenever these modulations hit the DVB API370		 *	case APSK_256:371		 *		mask = 0x7f;372		 *		break;373		 *	case APSK_128:374		 *		mask = 0x3f;375		 *		break;376		 *	case APSK_64:377		 *		mask = 0x1f;378		 *		break;379		 */380		case APSK_32:381			mask = 0x0f;382			break;383		case APSK_16:384			mask = 0x07;385			break;386		default:387			mask = 0x03;388			break;389		}390		stat = start(fe, 3, mask, ts_config);391	} else {392		stat = start_iq(fe, 0, 4, ts_config);393	}394	if (!stat) {395		state->started = 1;396		state->first_time_lock = 1;397		state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL;398	}399 400	return stat;401}402 403static int tune(struct dvb_frontend *fe, bool re_tune,404		unsigned int mode_flags,405		unsigned int *delay, enum fe_status *status)406{407	int r;408 409	if (re_tune) {410		r = set_parameters(fe);411		if (r)412			return r;413	}414	r = read_status(fe, status);415	if (r)416		return r;417 418	if (*status & FE_HAS_LOCK)419		return 0;420	*delay = HZ / 10;421	return 0;422}423 424static enum dvbfe_algo get_algo(struct dvb_frontend *fe)425{426	return DVBFE_ALGO_HW;427}428 429static int set_input(struct dvb_frontend *fe, int input)430{431	struct sx8 *state = fe->demodulator_priv;432	struct mci_base *mci_base = state->mci.base;433 434	if (input >= SX8_TUNER_NUM)435		return -EINVAL;436 437	state->mci.tuner = input;438	dev_dbg(mci_base->dev, "MCI-%d: input=%d\n", state->mci.nr, input);439	return 0;440}441 442static struct dvb_frontend_ops sx8_ops = {443	.delsys = { SYS_DVBS, SYS_DVBS2 },444	.info = {445		.name			= "Digital Devices MaxSX8 MCI DVB-S/S2/S2X",446		.frequency_min_hz	=  950 * MHz,447		.frequency_max_hz	= 2150 * MHz,448		.symbol_rate_min	= 100000,449		.symbol_rate_max	= 100000000,450		.caps			= FE_CAN_INVERSION_AUTO |451					  FE_CAN_FEC_AUTO       |452					  FE_CAN_QPSK           |453					  FE_CAN_2G_MODULATION  |454					  FE_CAN_MULTISTREAM,455	},456	.get_frontend_algo		= get_algo,457	.tune				= tune,458	.release			= release,459	.read_status			= read_status,460};461 462static int init(struct mci *mci)463{464	struct sx8 *state = (struct sx8 *)mci;465 466	state->mci.demod = SX8_DEMOD_NONE;467	return 0;468}469 470const struct mci_cfg ddb_max_sx8_cfg = {471	.type = 0,472	.fe_ops = &sx8_ops,473	.base_size = sizeof(struct sx8_base),474	.state_size = sizeof(struct sx8),475	.init = init,476	.set_input = set_input,477};478