brintos

brintos / linux-shallow public Read only

0
0
Text · 10.3 KiB · bc72d95 Raw
472 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Abilis Systems Single DVB-T Receiver4 * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>5 * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>6 */7 8#include <media/dvb_frontend.h>9 10#include "as102_fe.h"11 12struct as102_state {13	struct dvb_frontend frontend;14	struct as10x_demod_stats demod_stats;15 16	const struct as102_fe_ops *ops;17	void *priv;18	uint8_t elna_cfg;19 20	/* signal strength */21	uint16_t signal_strength;22	/* bit error rate */23	uint32_t ber;24};25 26static uint8_t as102_fe_get_code_rate(enum fe_code_rate arg)27{28	uint8_t c;29 30	switch (arg) {31	case FEC_1_2:32		c = CODE_RATE_1_2;33		break;34	case FEC_2_3:35		c = CODE_RATE_2_3;36		break;37	case FEC_3_4:38		c = CODE_RATE_3_4;39		break;40	case FEC_5_6:41		c = CODE_RATE_5_6;42		break;43	case FEC_7_8:44		c = CODE_RATE_7_8;45		break;46	default:47		c = CODE_RATE_UNKNOWN;48		break;49	}50 51	return c;52}53 54static int as102_fe_set_frontend(struct dvb_frontend *fe)55{56	struct as102_state *state = fe->demodulator_priv;57	struct dtv_frontend_properties *c = &fe->dtv_property_cache;58	struct as10x_tune_args tune_args = { 0 };59 60	/* set frequency */61	tune_args.freq = c->frequency / 1000;62 63	/* fix interleaving_mode */64	tune_args.interleaving_mode = INTLV_NATIVE;65 66	switch (c->bandwidth_hz) {67	case 8000000:68		tune_args.bandwidth = BW_8_MHZ;69		break;70	case 7000000:71		tune_args.bandwidth = BW_7_MHZ;72		break;73	case 6000000:74		tune_args.bandwidth = BW_6_MHZ;75		break;76	default:77		tune_args.bandwidth = BW_8_MHZ;78	}79 80	switch (c->guard_interval) {81	case GUARD_INTERVAL_1_32:82		tune_args.guard_interval = GUARD_INT_1_32;83		break;84	case GUARD_INTERVAL_1_16:85		tune_args.guard_interval = GUARD_INT_1_16;86		break;87	case GUARD_INTERVAL_1_8:88		tune_args.guard_interval = GUARD_INT_1_8;89		break;90	case GUARD_INTERVAL_1_4:91		tune_args.guard_interval = GUARD_INT_1_4;92		break;93	case GUARD_INTERVAL_AUTO:94	default:95		tune_args.guard_interval = GUARD_UNKNOWN;96		break;97	}98 99	switch (c->modulation) {100	case QPSK:101		tune_args.modulation = CONST_QPSK;102		break;103	case QAM_16:104		tune_args.modulation = CONST_QAM16;105		break;106	case QAM_64:107		tune_args.modulation = CONST_QAM64;108		break;109	default:110		tune_args.modulation = CONST_UNKNOWN;111		break;112	}113 114	switch (c->transmission_mode) {115	case TRANSMISSION_MODE_2K:116		tune_args.transmission_mode = TRANS_MODE_2K;117		break;118	case TRANSMISSION_MODE_8K:119		tune_args.transmission_mode = TRANS_MODE_8K;120		break;121	default:122		tune_args.transmission_mode = TRANS_MODE_UNKNOWN;123	}124 125	switch (c->hierarchy) {126	case HIERARCHY_NONE:127		tune_args.hierarchy = HIER_NONE;128		break;129	case HIERARCHY_1:130		tune_args.hierarchy = HIER_ALPHA_1;131		break;132	case HIERARCHY_2:133		tune_args.hierarchy = HIER_ALPHA_2;134		break;135	case HIERARCHY_4:136		tune_args.hierarchy = HIER_ALPHA_4;137		break;138	case HIERARCHY_AUTO:139		tune_args.hierarchy = HIER_UNKNOWN;140		break;141	}142 143	pr_debug("as102: tuner parameters: freq: %d  bw: 0x%02x  gi: 0x%02x\n",144			c->frequency,145			tune_args.bandwidth,146			tune_args.guard_interval);147 148	/*149	 * Detect a hierarchy selection150	 * if HP/LP are both set to FEC_NONE, HP will be selected.151	 */152	if ((tune_args.hierarchy != HIER_NONE) &&153		       ((c->code_rate_LP == FEC_NONE) ||154			(c->code_rate_HP == FEC_NONE))) {155 156		if (c->code_rate_LP == FEC_NONE) {157			tune_args.hier_select = HIER_HIGH_PRIORITY;158			tune_args.code_rate =159			   as102_fe_get_code_rate(c->code_rate_HP);160		}161 162		if (c->code_rate_HP == FEC_NONE) {163			tune_args.hier_select = HIER_LOW_PRIORITY;164			tune_args.code_rate =165			   as102_fe_get_code_rate(c->code_rate_LP);166		}167 168		pr_debug("as102: \thierarchy: 0x%02x  selected: %s  code_rate_%s: 0x%02x\n",169			tune_args.hierarchy,170			tune_args.hier_select == HIER_HIGH_PRIORITY ?171			"HP" : "LP",172			tune_args.hier_select == HIER_HIGH_PRIORITY ?173			"HP" : "LP",174			tune_args.code_rate);175	} else {176		tune_args.code_rate =177			as102_fe_get_code_rate(c->code_rate_HP);178	}179 180	/* Set frontend arguments */181	return state->ops->set_tune(state->priv, &tune_args);182}183 184static int as102_fe_get_frontend(struct dvb_frontend *fe,185				 struct dtv_frontend_properties *c)186{187	struct as102_state *state = fe->demodulator_priv;188	int ret = 0;189	struct as10x_tps tps = { 0 };190 191	/* send abilis command: GET_TPS */192	ret = state->ops->get_tps(state->priv, &tps);193	if (ret < 0)194		return ret;195 196	/* extract constellation */197	switch (tps.modulation) {198	case CONST_QPSK:199		c->modulation = QPSK;200		break;201	case CONST_QAM16:202		c->modulation = QAM_16;203		break;204	case CONST_QAM64:205		c->modulation = QAM_64;206		break;207	}208 209	/* extract hierarchy */210	switch (tps.hierarchy) {211	case HIER_NONE:212		c->hierarchy = HIERARCHY_NONE;213		break;214	case HIER_ALPHA_1:215		c->hierarchy = HIERARCHY_1;216		break;217	case HIER_ALPHA_2:218		c->hierarchy = HIERARCHY_2;219		break;220	case HIER_ALPHA_4:221		c->hierarchy = HIERARCHY_4;222		break;223	}224 225	/* extract code rate HP */226	switch (tps.code_rate_HP) {227	case CODE_RATE_1_2:228		c->code_rate_HP = FEC_1_2;229		break;230	case CODE_RATE_2_3:231		c->code_rate_HP = FEC_2_3;232		break;233	case CODE_RATE_3_4:234		c->code_rate_HP = FEC_3_4;235		break;236	case CODE_RATE_5_6:237		c->code_rate_HP = FEC_5_6;238		break;239	case CODE_RATE_7_8:240		c->code_rate_HP = FEC_7_8;241		break;242	}243 244	/* extract code rate LP */245	switch (tps.code_rate_LP) {246	case CODE_RATE_1_2:247		c->code_rate_LP = FEC_1_2;248		break;249	case CODE_RATE_2_3:250		c->code_rate_LP = FEC_2_3;251		break;252	case CODE_RATE_3_4:253		c->code_rate_LP = FEC_3_4;254		break;255	case CODE_RATE_5_6:256		c->code_rate_LP = FEC_5_6;257		break;258	case CODE_RATE_7_8:259		c->code_rate_LP = FEC_7_8;260		break;261	}262 263	/* extract guard interval */264	switch (tps.guard_interval) {265	case GUARD_INT_1_32:266		c->guard_interval = GUARD_INTERVAL_1_32;267		break;268	case GUARD_INT_1_16:269		c->guard_interval = GUARD_INTERVAL_1_16;270		break;271	case GUARD_INT_1_8:272		c->guard_interval = GUARD_INTERVAL_1_8;273		break;274	case GUARD_INT_1_4:275		c->guard_interval = GUARD_INTERVAL_1_4;276		break;277	}278 279	/* extract transmission mode */280	switch (tps.transmission_mode) {281	case TRANS_MODE_2K:282		c->transmission_mode = TRANSMISSION_MODE_2K;283		break;284	case TRANS_MODE_8K:285		c->transmission_mode = TRANSMISSION_MODE_8K;286		break;287	}288 289	return 0;290}291 292static int as102_fe_get_tune_settings(struct dvb_frontend *fe,293			struct dvb_frontend_tune_settings *settings)294{295 296	settings->min_delay_ms = 1000;297 298	return 0;299}300 301static int as102_fe_read_status(struct dvb_frontend *fe, enum fe_status *status)302{303	int ret = 0;304	struct as102_state *state = fe->demodulator_priv;305	struct as10x_tune_status tstate = { 0 };306 307	/* send abilis command: GET_TUNE_STATUS */308	ret = state->ops->get_status(state->priv, &tstate);309	if (ret < 0)310		return ret;311 312	state->signal_strength  = tstate.signal_strength;313	state->ber  = tstate.BER;314 315	switch (tstate.tune_state) {316	case TUNE_STATUS_SIGNAL_DVB_OK:317		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;318		break;319	case TUNE_STATUS_STREAM_DETECTED:320		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |321			  FE_HAS_VITERBI;322		break;323	case TUNE_STATUS_STREAM_TUNED:324		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |325			  FE_HAS_LOCK | FE_HAS_VITERBI;326		break;327	default:328		*status = TUNE_STATUS_NOT_TUNED;329	}330 331	pr_debug("as102: tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",332		 tstate.tune_state, tstate.signal_strength,333		 tstate.PER, tstate.BER);334 335	if (!(*status & FE_HAS_LOCK)) {336		memset(&state->demod_stats, 0, sizeof(state->demod_stats));337		return 0;338	}339 340	ret = state->ops->get_stats(state->priv, &state->demod_stats);341	if (ret < 0)342		memset(&state->demod_stats, 0, sizeof(state->demod_stats));343 344	return ret;345}346 347/*348 * Note:349 * - in AS102 SNR=MER350 *   - the SNR will be returned in linear terms, i.e. not in dB351 *   - the accuracy equals ±2dB for a SNR range from 4dB to 30dB352 *   - the accuracy is >2dB for SNR values outside this range353 */354static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr)355{356	struct as102_state *state = fe->demodulator_priv;357 358	*snr = state->demod_stats.mer;359 360	return 0;361}362 363static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber)364{365	struct as102_state *state = fe->demodulator_priv;366 367	*ber = state->ber;368 369	return 0;370}371 372static int as102_fe_read_signal_strength(struct dvb_frontend *fe,373					 u16 *strength)374{375	struct as102_state *state = fe->demodulator_priv;376 377	*strength = (((0xffff * 400) * state->signal_strength + 41000) * 2);378 379	return 0;380}381 382static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)383{384	struct as102_state *state = fe->demodulator_priv;385 386	if (state->demod_stats.has_started)387		*ucblocks = state->demod_stats.bad_frame_count;388	else389		*ucblocks = 0;390 391	return 0;392}393 394static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)395{396	struct as102_state *state = fe->demodulator_priv;397 398	return state->ops->stream_ctrl(state->priv, acquire,399				      state->elna_cfg);400}401 402static void as102_fe_release(struct dvb_frontend *fe)403{404	struct as102_state *state = fe->demodulator_priv;405 406	kfree(state);407}408 409 410static const struct dvb_frontend_ops as102_fe_ops = {411	.delsys = { SYS_DVBT },412	.info = {413		.name			= "Abilis AS102 DVB-T",414		.frequency_min_hz	= 174 * MHz,415		.frequency_max_hz	= 862 * MHz,416		.frequency_stepsize_hz	= 166667,417		.caps = FE_CAN_INVERSION_AUTO418			| FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4419			| FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO420			| FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK421			| FE_CAN_QAM_AUTO422			| FE_CAN_TRANSMISSION_MODE_AUTO423			| FE_CAN_GUARD_INTERVAL_AUTO424			| FE_CAN_HIERARCHY_AUTO425			| FE_CAN_RECOVER426			| FE_CAN_MUTE_TS427	},428 429	.set_frontend		= as102_fe_set_frontend,430	.get_frontend		= as102_fe_get_frontend,431	.get_tune_settings	= as102_fe_get_tune_settings,432 433	.read_status		= as102_fe_read_status,434	.read_snr		= as102_fe_read_snr,435	.read_ber		= as102_fe_read_ber,436	.read_signal_strength	= as102_fe_read_signal_strength,437	.read_ucblocks		= as102_fe_read_ucblocks,438	.ts_bus_ctrl		= as102_fe_ts_bus_ctrl,439	.release		= as102_fe_release,440};441 442struct dvb_frontend *as102_attach(const char *name,443				  const struct as102_fe_ops *ops,444				  void *priv,445				  uint8_t elna_cfg)446{447	struct as102_state *state;448	struct dvb_frontend *fe;449 450	state = kzalloc(sizeof(*state), GFP_KERNEL);451	if (!state)452		return NULL;453 454	fe = &state->frontend;455	fe->demodulator_priv = state;456	state->ops = ops;457	state->priv = priv;458	state->elna_cfg = elna_cfg;459 460	/* init frontend callback ops */461	memcpy(&fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops));462	strscpy(fe->ops.info.name, name, sizeof(fe->ops.info.name));463 464	return fe;465 466}467EXPORT_SYMBOL_GPL(as102_attach);468 469MODULE_DESCRIPTION("as102-fe");470MODULE_LICENSE("GPL");471MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");472