brintos

brintos / linux-shallow public Read only

0
0
Text · 33.1 KiB · 44d8fe8 Raw
1272 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/****************************************************************3 4Siano Mobile Silicon, Inc.5MDTV receiver kernel modules.6Copyright (C) 2006-2008, Uri Shkolnik7 8 9****************************************************************/10 11#include "smscoreapi.h"12 13#include <linux/module.h>14#include <linux/slab.h>15#include <linux/init.h>16#include <asm/div64.h>17 18#include <media/dmxdev.h>19#include <media/dvbdev.h>20#include <media/dvb_demux.h>21#include <media/dvb_frontend.h>22 23#include "sms-cards.h"24 25#include "smsdvb.h"26 27DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);28 29static LIST_HEAD(g_smsdvb_clients);30static DEFINE_MUTEX(g_smsdvb_clientslock);31 32static u32 sms_to_guard_interval_table[] = {33	[0] = GUARD_INTERVAL_1_32,34	[1] = GUARD_INTERVAL_1_16,35	[2] = GUARD_INTERVAL_1_8,36	[3] = GUARD_INTERVAL_1_4,37};38 39static u32 sms_to_code_rate_table[] = {40	[0] = FEC_1_2,41	[1] = FEC_2_3,42	[2] = FEC_3_4,43	[3] = FEC_5_6,44	[4] = FEC_7_8,45};46 47 48static u32 sms_to_hierarchy_table[] = {49	[0] = HIERARCHY_NONE,50	[1] = HIERARCHY_1,51	[2] = HIERARCHY_2,52	[3] = HIERARCHY_4,53};54 55static u32 sms_to_modulation_table[] = {56	[0] = QPSK,57	[1] = QAM_16,58	[2] = QAM_64,59	[3] = DQPSK,60};61 62 63/* Events that may come from DVB v3 adapter */64static void sms_board_dvb3_event(struct smsdvb_client_t *client,65		enum SMS_DVB3_EVENTS event) {66 67	struct smscore_device_t *coredev = client->coredev;68	switch (event) {69	case DVB3_EVENT_INIT:70		pr_debug("DVB3_EVENT_INIT\n");71		sms_board_event(coredev, BOARD_EVENT_BIND);72		break;73	case DVB3_EVENT_SLEEP:74		pr_debug("DVB3_EVENT_SLEEP\n");75		sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND);76		break;77	case DVB3_EVENT_HOTPLUG:78		pr_debug("DVB3_EVENT_HOTPLUG\n");79		sms_board_event(coredev, BOARD_EVENT_POWER_INIT);80		break;81	case DVB3_EVENT_FE_LOCK:82		if (client->event_fe_state != DVB3_EVENT_FE_LOCK) {83			client->event_fe_state = DVB3_EVENT_FE_LOCK;84			pr_debug("DVB3_EVENT_FE_LOCK\n");85			sms_board_event(coredev, BOARD_EVENT_FE_LOCK);86		}87		break;88	case DVB3_EVENT_FE_UNLOCK:89		if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) {90			client->event_fe_state = DVB3_EVENT_FE_UNLOCK;91			pr_debug("DVB3_EVENT_FE_UNLOCK\n");92			sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK);93		}94		break;95	case DVB3_EVENT_UNC_OK:96		if (client->event_unc_state != DVB3_EVENT_UNC_OK) {97			client->event_unc_state = DVB3_EVENT_UNC_OK;98			pr_debug("DVB3_EVENT_UNC_OK\n");99			sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK);100		}101		break;102	case DVB3_EVENT_UNC_ERR:103		if (client->event_unc_state != DVB3_EVENT_UNC_ERR) {104			client->event_unc_state = DVB3_EVENT_UNC_ERR;105			pr_debug("DVB3_EVENT_UNC_ERR\n");106			sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS);107		}108		break;109 110	default:111		pr_err("Unknown dvb3 api event\n");112		break;113	}114}115 116static void smsdvb_stats_not_ready(struct dvb_frontend *fe)117{118	struct smsdvb_client_t *client =119		container_of(fe, struct smsdvb_client_t, frontend);120	struct smscore_device_t *coredev = client->coredev;121	struct dtv_frontend_properties *c = &fe->dtv_property_cache;122	int i, n_layers;123 124	switch (smscore_get_device_mode(coredev)) {125	case DEVICE_MODE_ISDBT:126	case DEVICE_MODE_ISDBT_BDA:127		n_layers = 4;128		break;129	default:130		n_layers = 1;131	}132 133	/* Global stats */134	c->strength.len = 1;135	c->cnr.len = 1;136	c->strength.stat[0].scale = FE_SCALE_DECIBEL;137	c->cnr.stat[0].scale = FE_SCALE_DECIBEL;138 139	/* Per-layer stats */140	c->post_bit_error.len = n_layers;141	c->post_bit_count.len = n_layers;142	c->block_error.len = n_layers;143	c->block_count.len = n_layers;144 145	/*146	 * Put all of them at FE_SCALE_NOT_AVAILABLE. They're dynamically147	 * changed when the stats become available.148	 */149	for (i = 0; i < n_layers; i++) {150		c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;151		c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;152		c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;153		c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;154	}155}156 157static inline int sms_to_mode(u32 mode)158{159	switch (mode) {160	case 2:161		return TRANSMISSION_MODE_2K;162	case 4:163		return TRANSMISSION_MODE_4K;164	case 8:165		return TRANSMISSION_MODE_8K;166	}167	return TRANSMISSION_MODE_AUTO;168}169 170static inline int sms_to_isdbt_mode(u32 mode)171{172	switch (mode) {173	case 1:174		return TRANSMISSION_MODE_2K;175	case 2:176		return TRANSMISSION_MODE_4K;177	case 3:178		return TRANSMISSION_MODE_8K;179	}180	return TRANSMISSION_MODE_AUTO;181}182 183static inline int sms_to_isdbt_guard_interval(u32 interval)184{185	switch (interval) {186	case 4:187		return GUARD_INTERVAL_1_4;188	case 8:189		return GUARD_INTERVAL_1_8;190	case 16:191		return GUARD_INTERVAL_1_16;192	case 32:193		return GUARD_INTERVAL_1_32;194	}195	return GUARD_INTERVAL_AUTO;196}197 198static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked)199{200	if (is_demod_locked)201		return FE_HAS_SIGNAL  | FE_HAS_CARRIER | FE_HAS_VITERBI |202		       FE_HAS_SYNC    | FE_HAS_LOCK;203 204	if (is_rf_locked)205		return FE_HAS_SIGNAL | FE_HAS_CARRIER;206 207	return 0;208}209 210static inline u32 sms_to_bw(u32 value)211{212	return value * 1000000;213}214 215#define convert_from_table(value, table, defval) ({			\216	u32 __ret;							\217	if (value < ARRAY_SIZE(table))					\218		__ret = table[value];					\219	else								\220		__ret = defval;						\221	__ret;								\222})223 224#define sms_to_guard_interval(value)					\225	convert_from_table(value, sms_to_guard_interval_table,		\226			   GUARD_INTERVAL_AUTO);227 228#define sms_to_code_rate(value)						\229	convert_from_table(value, sms_to_code_rate_table,		\230			   FEC_NONE);231 232#define sms_to_hierarchy(value)						\233	convert_from_table(value, sms_to_hierarchy_table,		\234			   FEC_NONE);235 236#define sms_to_modulation(value)					\237	convert_from_table(value, sms_to_modulation_table,		\238			   FEC_NONE);239 240static void smsdvb_update_tx_params(struct smsdvb_client_t *client,241				    struct sms_tx_stats *p)242{243	struct dvb_frontend *fe = &client->frontend;244	struct dtv_frontend_properties *c = &fe->dtv_property_cache;245 246	c->frequency = p->frequency;247	client->fe_status = sms_to_status(p->is_demod_locked, 0);248	c->bandwidth_hz = sms_to_bw(p->bandwidth);249	c->transmission_mode = sms_to_mode(p->transmission_mode);250	c->guard_interval = sms_to_guard_interval(p->guard_interval);251	c->code_rate_HP = sms_to_code_rate(p->code_rate);252	c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);253	c->hierarchy = sms_to_hierarchy(p->hierarchy);254	c->modulation = sms_to_modulation(p->constellation);255}256 257static void smsdvb_update_per_slices(struct smsdvb_client_t *client,258				     struct RECEPTION_STATISTICS_PER_SLICES_S *p)259{260	struct dvb_frontend *fe = &client->frontend;261	struct dtv_frontend_properties *c = &fe->dtv_property_cache;262	u64 tmp;263 264	client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);265	c->modulation = sms_to_modulation(p->constellation);266 267	/* signal Strength, in DBm */268	c->strength.stat[0].uvalue = p->in_band_power * 1000;269 270	/* Carrier to noise ratio, in DB */271	c->cnr.stat[0].svalue = p->snr * 1000;272 273	/* PER/BER requires demod lock */274	if (!p->is_demod_locked)275		return;276 277	/* TS PER */278	client->last_per = c->block_error.stat[0].uvalue;279	c->block_error.stat[0].scale = FE_SCALE_COUNTER;280	c->block_count.stat[0].scale = FE_SCALE_COUNTER;281	c->block_error.stat[0].uvalue += p->ets_packets;282	c->block_count.stat[0].uvalue += p->ets_packets + p->ts_packets;283 284	/* ber */285	c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;286	c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;287	c->post_bit_error.stat[0].uvalue += p->ber_error_count;288	c->post_bit_count.stat[0].uvalue += p->ber_bit_count;289 290	/* Legacy PER/BER */291	tmp = p->ets_packets * 65535ULL;292	if (p->ts_packets + p->ets_packets)293		do_div(tmp, p->ts_packets + p->ets_packets);294	client->legacy_per = tmp;295}296 297static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client,298				    struct sms_stats *p)299{300	struct dvb_frontend *fe = &client->frontend;301	struct dtv_frontend_properties *c = &fe->dtv_property_cache;302 303	if (client->prt_dvb_stats)304		client->prt_dvb_stats(client->debug_data, p);305 306	client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);307 308	/* Update DVB modulation parameters */309	c->frequency = p->frequency;310	client->fe_status = sms_to_status(p->is_demod_locked, 0);311	c->bandwidth_hz = sms_to_bw(p->bandwidth);312	c->transmission_mode = sms_to_mode(p->transmission_mode);313	c->guard_interval = sms_to_guard_interval(p->guard_interval);314	c->code_rate_HP = sms_to_code_rate(p->code_rate);315	c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);316	c->hierarchy = sms_to_hierarchy(p->hierarchy);317	c->modulation = sms_to_modulation(p->constellation);318 319	/* update reception data */320	c->lna = p->is_external_lna_on ? 1 : 0;321 322	/* Carrier to noise ratio, in DB */323	c->cnr.stat[0].svalue = p->SNR * 1000;324 325	/* signal Strength, in DBm */326	c->strength.stat[0].uvalue = p->in_band_pwr * 1000;327 328	/* PER/BER requires demod lock */329	if (!p->is_demod_locked)330		return;331 332	/* TS PER */333	client->last_per = c->block_error.stat[0].uvalue;334	c->block_error.stat[0].scale = FE_SCALE_COUNTER;335	c->block_count.stat[0].scale = FE_SCALE_COUNTER;336	c->block_error.stat[0].uvalue += p->error_ts_packets;337	c->block_count.stat[0].uvalue += p->total_ts_packets;338 339	/* ber */340	c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;341	c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;342	c->post_bit_error.stat[0].uvalue += p->ber_error_count;343	c->post_bit_count.stat[0].uvalue += p->ber_bit_count;344 345	/* Legacy PER/BER */346	client->legacy_ber = p->ber;347};348 349static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client,350				      struct sms_isdbt_stats *p)351{352	struct dvb_frontend *fe = &client->frontend;353	struct dtv_frontend_properties *c = &fe->dtv_property_cache;354	struct sms_isdbt_layer_stats *lr;355	int i, n_layers;356 357	if (client->prt_isdb_stats)358		client->prt_isdb_stats(client->debug_data, p);359 360	client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);361 362	/*363	 * Firmware 2.1 seems to report only lock status and364	 * signal strength. The signal strength indicator is at the365	 * wrong field.366	 */367	if (p->statistics_type == 0) {368		c->strength.stat[0].uvalue = ((s32)p->transmission_mode) * 1000;369		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;370		return;371	}372 373	/* Update ISDB-T transmission parameters */374	c->frequency = p->frequency;375	c->bandwidth_hz = sms_to_bw(p->bandwidth);376	c->transmission_mode = sms_to_isdbt_mode(p->transmission_mode);377	c->guard_interval = sms_to_isdbt_guard_interval(p->guard_interval);378	c->isdbt_partial_reception = p->partial_reception ? 1 : 0;379	n_layers = p->num_of_layers;380	if (n_layers < 1)381		n_layers = 1;382	if (n_layers > 3)383		n_layers = 3;384	c->isdbt_layer_enabled = 0;385 386	/* update reception data */387	c->lna = p->is_external_lna_on ? 1 : 0;388 389	/* Carrier to noise ratio, in DB */390	c->cnr.stat[0].svalue = p->SNR * 1000;391 392	/* signal Strength, in DBm */393	c->strength.stat[0].uvalue = p->in_band_pwr * 1000;394 395	/* PER/BER and per-layer stats require demod lock */396	if (!p->is_demod_locked)397		return;398 399	client->last_per = c->block_error.stat[0].uvalue;400 401	/* Clears global counters, as the code below will sum it again */402	c->block_error.stat[0].uvalue = 0;403	c->block_count.stat[0].uvalue = 0;404	c->block_error.stat[0].scale = FE_SCALE_COUNTER;405	c->block_count.stat[0].scale = FE_SCALE_COUNTER;406	c->post_bit_error.stat[0].uvalue = 0;407	c->post_bit_count.stat[0].uvalue = 0;408	c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;409	c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;410 411	for (i = 0; i < n_layers; i++) {412		lr = &p->layer_info[i];413 414		/* Update per-layer transmission parameters */415		if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {416			c->isdbt_layer_enabled |= 1 << i;417			c->layer[i].segment_count = lr->number_of_segments;418		} else {419			continue;420		}421		c->layer[i].modulation = sms_to_modulation(lr->constellation);422		c->layer[i].fec = sms_to_code_rate(lr->code_rate);423 424		/* Time interleaving */425		c->layer[i].interleaving = (u8)lr->ti_ldepth_i;426 427		/* TS PER */428		c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;429		c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;430		c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;431		c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;432 433		/* Update global PER counter */434		c->block_error.stat[0].uvalue += lr->error_ts_packets;435		c->block_count.stat[0].uvalue += lr->total_ts_packets;436 437		/* BER */438		c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;439		c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;440		c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;441		c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;442 443		/* Update global BER counter */444		c->post_bit_error.stat[0].uvalue += lr->ber_error_count;445		c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;446	}447}448 449static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client,450					 struct sms_isdbt_stats_ex *p)451{452	struct dvb_frontend *fe = &client->frontend;453	struct dtv_frontend_properties *c = &fe->dtv_property_cache;454	struct sms_isdbt_layer_stats *lr;455	int i, n_layers;456 457	if (client->prt_isdb_stats_ex)458		client->prt_isdb_stats_ex(client->debug_data, p);459 460	/* Update ISDB-T transmission parameters */461	c->frequency = p->frequency;462	client->fe_status = sms_to_status(p->is_demod_locked, 0);463	c->bandwidth_hz = sms_to_bw(p->bandwidth);464	c->transmission_mode = sms_to_isdbt_mode(p->transmission_mode);465	c->guard_interval = sms_to_isdbt_guard_interval(p->guard_interval);466	c->isdbt_partial_reception = p->partial_reception ? 1 : 0;467	n_layers = p->num_of_layers;468	if (n_layers < 1)469		n_layers = 1;470	if (n_layers > 3)471		n_layers = 3;472	c->isdbt_layer_enabled = 0;473 474	/* update reception data */475	c->lna = p->is_external_lna_on ? 1 : 0;476 477	/* Carrier to noise ratio, in DB */478	c->cnr.stat[0].svalue = p->SNR * 1000;479 480	/* signal Strength, in DBm */481	c->strength.stat[0].uvalue = p->in_band_pwr * 1000;482 483	/* PER/BER and per-layer stats require demod lock */484	if (!p->is_demod_locked)485		return;486 487	client->last_per = c->block_error.stat[0].uvalue;488 489	/* Clears global counters, as the code below will sum it again */490	c->block_error.stat[0].uvalue = 0;491	c->block_count.stat[0].uvalue = 0;492	c->block_error.stat[0].scale = FE_SCALE_COUNTER;493	c->block_count.stat[0].scale = FE_SCALE_COUNTER;494	c->post_bit_error.stat[0].uvalue = 0;495	c->post_bit_count.stat[0].uvalue = 0;496	c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;497	c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;498 499	c->post_bit_error.len = n_layers + 1;500	c->post_bit_count.len = n_layers + 1;501	c->block_error.len = n_layers + 1;502	c->block_count.len = n_layers + 1;503	for (i = 0; i < n_layers; i++) {504		lr = &p->layer_info[i];505 506		/* Update per-layer transmission parameters */507		if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {508			c->isdbt_layer_enabled |= 1 << i;509			c->layer[i].segment_count = lr->number_of_segments;510		} else {511			continue;512		}513		c->layer[i].modulation = sms_to_modulation(lr->constellation);514		c->layer[i].fec = sms_to_code_rate(lr->code_rate);515 516		/* Time interleaving */517		c->layer[i].interleaving = (u8)lr->ti_ldepth_i;518 519		/* TS PER */520		c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;521		c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;522		c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;523		c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;524 525		/* Update global PER counter */526		c->block_error.stat[0].uvalue += lr->error_ts_packets;527		c->block_count.stat[0].uvalue += lr->total_ts_packets;528 529		/* ber */530		c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;531		c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;532		c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;533		c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;534 535		/* Update global ber counter */536		c->post_bit_error.stat[0].uvalue += lr->ber_error_count;537		c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;538	}539}540 541static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)542{543	struct smsdvb_client_t *client = (struct smsdvb_client_t *) context;544	struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) (((u8 *) cb->p)545			+ cb->offset);546	void *p = phdr + 1;547	struct dvb_frontend *fe = &client->frontend;548	struct dtv_frontend_properties *c = &fe->dtv_property_cache;549	bool is_status_update = false;550 551	switch (phdr->msg_type) {552	case MSG_SMS_DVBT_BDA_DATA:553		/*554		 * Only feed data to dvb demux if are there any feed listening555		 * to it and if the device has tuned556		 */557		if (client->feed_users && client->has_tuned)558			dvb_dmx_swfilter(&client->demux, p,559					 cb->size - sizeof(struct sms_msg_hdr));560		break;561 562	case MSG_SMS_RF_TUNE_RES:563	case MSG_SMS_ISDBT_TUNE_RES:564		complete(&client->tune_done);565		break;566 567	case MSG_SMS_SIGNAL_DETECTED_IND:568		client->fe_status = FE_HAS_SIGNAL  | FE_HAS_CARRIER |569				    FE_HAS_VITERBI | FE_HAS_SYNC    |570				    FE_HAS_LOCK;571 572		is_status_update = true;573		break;574 575	case MSG_SMS_NO_SIGNAL_IND:576		client->fe_status = 0;577 578		is_status_update = true;579		break;580 581	case MSG_SMS_TRANSMISSION_IND:582		smsdvb_update_tx_params(client, p);583 584		is_status_update = true;585		break;586 587	case MSG_SMS_HO_PER_SLICES_IND:588		smsdvb_update_per_slices(client, p);589 590		is_status_update = true;591		break;592 593	case MSG_SMS_GET_STATISTICS_RES:594		switch (smscore_get_device_mode(client->coredev)) {595		case DEVICE_MODE_ISDBT:596		case DEVICE_MODE_ISDBT_BDA:597			smsdvb_update_isdbt_stats(client, p);598			break;599		default:600			/* Skip sms_msg_statistics_info:request_result field */601			smsdvb_update_dvb_stats(client, p + sizeof(u32));602		}603 604		is_status_update = true;605		break;606 607	/* Only for ISDB-T */608	case MSG_SMS_GET_STATISTICS_EX_RES:609		/* Skip sms_msg_statistics_info:request_result field? */610		smsdvb_update_isdbt_stats_ex(client, p + sizeof(u32));611		is_status_update = true;612		break;613	default:614		pr_debug("message not handled\n");615	}616	smscore_putbuffer(client->coredev, cb);617 618	if (is_status_update) {619		if (client->fe_status & FE_HAS_LOCK) {620			sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK);621			if (client->last_per == c->block_error.stat[0].uvalue)622				sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK);623			else624				sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR);625			client->has_tuned = true;626		} else {627			smsdvb_stats_not_ready(fe);628			client->has_tuned = false;629			sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK);630		}631		complete(&client->stats_done);632	}633 634	return 0;635}636 637static void smsdvb_media_device_unregister(struct smsdvb_client_t *client)638{639#ifdef CONFIG_MEDIA_CONTROLLER_DVB640	struct smscore_device_t *coredev = client->coredev;641 642	if (!coredev->media_dev)643		return;644	media_device_unregister(coredev->media_dev);645	media_device_cleanup(coredev->media_dev);646	kfree(coredev->media_dev);647	coredev->media_dev = NULL;648#endif649}650 651static void smsdvb_unregister_client(struct smsdvb_client_t *client)652{653	/* must be called under clientslock */654 655	list_del(&client->entry);656 657	smsdvb_debugfs_release(client);658	smscore_unregister_client(client->smsclient);659	dvb_unregister_frontend(&client->frontend);660	dvb_dmxdev_release(&client->dmxdev);661	dvb_dmx_release(&client->demux);662	smsdvb_media_device_unregister(client);663	dvb_unregister_adapter(&client->adapter);664	kfree(client);665}666 667static void smsdvb_onremove(void *context)668{669	mutex_lock(&g_smsdvb_clientslock);670 671	smsdvb_unregister_client((struct smsdvb_client_t *) context);672 673	mutex_unlock(&g_smsdvb_clientslock);674}675 676static int smsdvb_start_feed(struct dvb_demux_feed *feed)677{678	struct smsdvb_client_t *client =679		container_of(feed->demux, struct smsdvb_client_t, demux);680	struct sms_msg_data pid_msg;681 682	pr_debug("add pid %d(%x)\n",683		  feed->pid, feed->pid);684 685	client->feed_users++;686 687	pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;688	pid_msg.x_msg_header.msg_dst_id = HIF_TASK;689	pid_msg.x_msg_header.msg_flags = 0;690	pid_msg.x_msg_header.msg_type  = MSG_SMS_ADD_PID_FILTER_REQ;691	pid_msg.x_msg_header.msg_length = sizeof(pid_msg);692	pid_msg.msg_data = feed->pid;693 694	return smsclient_sendrequest(client->smsclient,695				     &pid_msg, sizeof(pid_msg));696}697 698static int smsdvb_stop_feed(struct dvb_demux_feed *feed)699{700	struct smsdvb_client_t *client =701		container_of(feed->demux, struct smsdvb_client_t, demux);702	struct sms_msg_data pid_msg;703 704	pr_debug("remove pid %d(%x)\n",705		  feed->pid, feed->pid);706 707	client->feed_users--;708 709	pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;710	pid_msg.x_msg_header.msg_dst_id = HIF_TASK;711	pid_msg.x_msg_header.msg_flags = 0;712	pid_msg.x_msg_header.msg_type  = MSG_SMS_REMOVE_PID_FILTER_REQ;713	pid_msg.x_msg_header.msg_length = sizeof(pid_msg);714	pid_msg.msg_data = feed->pid;715 716	return smsclient_sendrequest(client->smsclient,717				     &pid_msg, sizeof(pid_msg));718}719 720static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client,721					void *buffer, size_t size,722					struct completion *completion)723{724	int rc;725 726	rc = smsclient_sendrequest(client->smsclient, buffer, size);727	if (rc < 0)728		return rc;729 730	return wait_for_completion_timeout(completion,731					   msecs_to_jiffies(2000)) ?732						0 : -ETIME;733}734 735static int smsdvb_send_statistics_request(struct smsdvb_client_t *client)736{737	int rc;738	struct sms_msg_hdr msg;739 740	/* Don't request stats too fast */741	if (client->get_stats_jiffies &&742	   (!time_after(jiffies, client->get_stats_jiffies)))743		return 0;744	client->get_stats_jiffies = jiffies + msecs_to_jiffies(100);745 746	msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;747	msg.msg_dst_id = HIF_TASK;748	msg.msg_flags = 0;749	msg.msg_length = sizeof(msg);750 751	switch (smscore_get_device_mode(client->coredev)) {752	case DEVICE_MODE_ISDBT:753	case DEVICE_MODE_ISDBT_BDA:754		/*755		* Check for firmware version, to avoid breaking for old cards756		*/757		if (client->coredev->fw_version >= 0x800)758			msg.msg_type = MSG_SMS_GET_STATISTICS_EX_REQ;759		else760			msg.msg_type = MSG_SMS_GET_STATISTICS_REQ;761		break;762	default:763		msg.msg_type = MSG_SMS_GET_STATISTICS_REQ;764	}765 766	rc = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),767					 &client->stats_done);768 769	return rc;770}771 772static inline int led_feedback(struct smsdvb_client_t *client)773{774	if (!(client->fe_status & FE_HAS_LOCK))775		return sms_board_led_feedback(client->coredev, SMS_LED_OFF);776 777	return sms_board_led_feedback(client->coredev,778				     (client->legacy_ber == 0) ?779				     SMS_LED_HI : SMS_LED_LO);780}781 782static int smsdvb_read_status(struct dvb_frontend *fe, enum fe_status *stat)783{784	int rc;785	struct smsdvb_client_t *client;786	client = container_of(fe, struct smsdvb_client_t, frontend);787 788	rc = smsdvb_send_statistics_request(client);789 790	*stat = client->fe_status;791 792	led_feedback(client);793 794	return rc;795}796 797static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)798{799	int rc;800	struct smsdvb_client_t *client;801 802	client = container_of(fe, struct smsdvb_client_t, frontend);803 804	rc = smsdvb_send_statistics_request(client);805 806	*ber = client->legacy_ber;807 808	led_feedback(client);809 810	return rc;811}812 813static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)814{815	struct dtv_frontend_properties *c = &fe->dtv_property_cache;816	int rc;817	s32 power = (s32) c->strength.stat[0].uvalue;818	struct smsdvb_client_t *client;819 820	client = container_of(fe, struct smsdvb_client_t, frontend);821 822	rc = smsdvb_send_statistics_request(client);823 824	if (power < -95)825		*strength = 0;826		else if (power > -29)827			*strength = 65535;828		else829			*strength = (power + 95) * 65535 / 66;830 831	led_feedback(client);832 833	return rc;834}835 836static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)837{838	struct dtv_frontend_properties *c = &fe->dtv_property_cache;839	int rc;840	struct smsdvb_client_t *client;841 842	client = container_of(fe, struct smsdvb_client_t, frontend);843 844	rc = smsdvb_send_statistics_request(client);845 846	/* Preferred scale for SNR with legacy API: 0.1 dB */847	*snr = ((u32)c->cnr.stat[0].svalue) / 100;848 849	led_feedback(client);850 851	return rc;852}853 854static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)855{856	int rc;857	struct dtv_frontend_properties *c = &fe->dtv_property_cache;858	struct smsdvb_client_t *client;859 860	client = container_of(fe, struct smsdvb_client_t, frontend);861 862	rc = smsdvb_send_statistics_request(client);863 864	*ucblocks = c->block_error.stat[0].uvalue;865 866	led_feedback(client);867 868	return rc;869}870 871static int smsdvb_get_tune_settings(struct dvb_frontend *fe,872				    struct dvb_frontend_tune_settings *tune)873{874	pr_debug("\n");875 876	tune->min_delay_ms = 400;877	tune->step_size = 250000;878	tune->max_drift = 0;879	return 0;880}881 882static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe)883{884	struct dtv_frontend_properties *c = &fe->dtv_property_cache;885	struct smsdvb_client_t *client =886		container_of(fe, struct smsdvb_client_t, frontend);887 888	struct {889		struct sms_msg_hdr	msg;890		u32		Data[3];891	} msg;892 893	int ret;894 895	client->fe_status = 0;896	client->event_fe_state = -1;897	client->event_unc_state = -1;898	fe->dtv_property_cache.delivery_system = SYS_DVBT;899 900	msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;901	msg.msg.msg_dst_id = HIF_TASK;902	msg.msg.msg_flags = 0;903	msg.msg.msg_type = MSG_SMS_RF_TUNE_REQ;904	msg.msg.msg_length = sizeof(msg);905	msg.Data[0] = c->frequency;906	msg.Data[2] = 12000000;907 908	pr_debug("%s: freq %d band %d\n", __func__, c->frequency,909		 c->bandwidth_hz);910 911	switch (c->bandwidth_hz / 1000000) {912	case 8:913		msg.Data[1] = BW_8_MHZ;914		break;915	case 7:916		msg.Data[1] = BW_7_MHZ;917		break;918	case 6:919		msg.Data[1] = BW_6_MHZ;920		break;921	case 0:922		return -EOPNOTSUPP;923	default:924		return -EINVAL;925	}926	/* Disable LNA, if any. An error is returned if no LNA is present */927	ret = sms_board_lna_control(client->coredev, 0);928	if (ret == 0) {929		enum fe_status status;930 931		/* tune with LNA off at first */932		ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),933						  &client->tune_done);934 935		smsdvb_read_status(fe, &status);936 937		if (status & FE_HAS_LOCK)938			return ret;939 940		/* previous tune didn't lock - enable LNA and tune again */941		sms_board_lna_control(client->coredev, 1);942	}943 944	return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),945					   &client->tune_done);946}947 948static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe)949{950	struct dtv_frontend_properties *c = &fe->dtv_property_cache;951	struct smsdvb_client_t *client =952		container_of(fe, struct smsdvb_client_t, frontend);953	int board_id = smscore_get_board_id(client->coredev);954	struct sms_board *board = sms_get_board(board_id);955	enum sms_device_type_st type = board->type;956	int ret;957 958	struct {959		struct sms_msg_hdr	msg;960		u32		Data[4];961	} msg;962 963	fe->dtv_property_cache.delivery_system = SYS_ISDBT;964 965	msg.msg.msg_src_id  = DVBT_BDA_CONTROL_MSG_ID;966	msg.msg.msg_dst_id  = HIF_TASK;967	msg.msg.msg_flags  = 0;968	msg.msg.msg_type   = MSG_SMS_ISDBT_TUNE_REQ;969	msg.msg.msg_length = sizeof(msg);970 971	if (c->isdbt_sb_segment_idx == -1)972		c->isdbt_sb_segment_idx = 0;973 974	if (!c->isdbt_layer_enabled)975		c->isdbt_layer_enabled = 7;976 977	msg.Data[0] = c->frequency;978	msg.Data[1] = BW_ISDBT_1SEG;979	msg.Data[2] = 12000000;980	msg.Data[3] = c->isdbt_sb_segment_idx;981 982	if (c->isdbt_partial_reception) {983		if ((type == SMS_PELE || type == SMS_RIO) &&984		    c->isdbt_sb_segment_count > 3)985			msg.Data[1] = BW_ISDBT_13SEG;986		else if (c->isdbt_sb_segment_count > 1)987			msg.Data[1] = BW_ISDBT_3SEG;988	} else if (type == SMS_PELE || type == SMS_RIO)989		msg.Data[1] = BW_ISDBT_13SEG;990 991	c->bandwidth_hz = 6000000;992 993	pr_debug("freq %d segwidth %d segindex %d\n",994		 c->frequency, c->isdbt_sb_segment_count,995		 c->isdbt_sb_segment_idx);996 997	/* Disable LNA, if any. An error is returned if no LNA is present */998	ret = sms_board_lna_control(client->coredev, 0);999	if (ret == 0) {1000		enum fe_status status;1001 1002		/* tune with LNA off at first */1003		ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),1004						  &client->tune_done);1005 1006		smsdvb_read_status(fe, &status);1007 1008		if (status & FE_HAS_LOCK)1009			return ret;1010 1011		/* previous tune didn't lock - enable LNA and tune again */1012		sms_board_lna_control(client->coredev, 1);1013	}1014	return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),1015					   &client->tune_done);1016}1017 1018static int smsdvb_set_frontend(struct dvb_frontend *fe)1019{1020	struct dtv_frontend_properties *c = &fe->dtv_property_cache;1021	struct smsdvb_client_t *client =1022		container_of(fe, struct smsdvb_client_t, frontend);1023	struct smscore_device_t *coredev = client->coredev;1024 1025	smsdvb_stats_not_ready(fe);1026	c->strength.stat[0].uvalue = 0;1027	c->cnr.stat[0].uvalue = 0;1028 1029	client->has_tuned = false;1030 1031	switch (smscore_get_device_mode(coredev)) {1032	case DEVICE_MODE_DVBT:1033	case DEVICE_MODE_DVBT_BDA:1034		return smsdvb_dvbt_set_frontend(fe);1035	case DEVICE_MODE_ISDBT:1036	case DEVICE_MODE_ISDBT_BDA:1037		return smsdvb_isdbt_set_frontend(fe);1038	default:1039		return -EINVAL;1040	}1041}1042 1043static int smsdvb_init(struct dvb_frontend *fe)1044{1045	struct smsdvb_client_t *client =1046		container_of(fe, struct smsdvb_client_t, frontend);1047 1048	sms_board_power(client->coredev, 1);1049 1050	sms_board_dvb3_event(client, DVB3_EVENT_INIT);1051	return 0;1052}1053 1054static int smsdvb_sleep(struct dvb_frontend *fe)1055{1056	struct smsdvb_client_t *client =1057		container_of(fe, struct smsdvb_client_t, frontend);1058 1059	sms_board_led_feedback(client->coredev, SMS_LED_OFF);1060	sms_board_power(client->coredev, 0);1061 1062	sms_board_dvb3_event(client, DVB3_EVENT_SLEEP);1063 1064	return 0;1065}1066 1067static void smsdvb_release(struct dvb_frontend *fe)1068{1069	/* do nothing */1070}1071 1072static const struct dvb_frontend_ops smsdvb_fe_ops = {1073	.info = {1074		.name			= "Siano Mobile Digital MDTV Receiver",1075		.frequency_min_hz	=  44250 * kHz,1076		.frequency_max_hz	= 867250 * kHz,1077		.frequency_stepsize_hz	=    250 * kHz,1078		.caps = FE_CAN_INVERSION_AUTO |1079			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |1080			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |1081			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |1082			FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |1083			FE_CAN_GUARD_INTERVAL_AUTO |1084			FE_CAN_RECOVER |1085			FE_CAN_HIERARCHY_AUTO,1086	},1087 1088	.release = smsdvb_release,1089 1090	.set_frontend = smsdvb_set_frontend,1091	.get_tune_settings = smsdvb_get_tune_settings,1092 1093	.read_status = smsdvb_read_status,1094	.read_ber = smsdvb_read_ber,1095	.read_signal_strength = smsdvb_read_signal_strength,1096	.read_snr = smsdvb_read_snr,1097	.read_ucblocks = smsdvb_read_ucblocks,1098 1099	.init = smsdvb_init,1100	.sleep = smsdvb_sleep,1101};1102 1103static int smsdvb_hotplug(struct smscore_device_t *coredev,1104			  struct device *device, int arrival)1105{1106	struct smsclient_params_t params;1107	struct smsdvb_client_t *client;1108	int rc;1109 1110	/* device removal handled by onremove callback */1111	if (!arrival)1112		return 0;1113	client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL);1114	if (!client)1115		return -ENOMEM;1116 1117	/* register dvb adapter */1118	rc = dvb_register_adapter(&client->adapter,1119				  sms_get_board(1120					smscore_get_board_id(coredev))->name,1121				  THIS_MODULE, device, adapter_nr);1122	if (rc < 0) {1123		pr_err("dvb_register_adapter() failed %d\n", rc);1124		goto adapter_error;1125	}1126	dvb_register_media_controller(&client->adapter, coredev->media_dev);1127 1128	/* init dvb demux */1129	client->demux.dmx.capabilities = DMX_TS_FILTERING;1130	client->demux.filternum = 32; /* todo: nova ??? */1131	client->demux.feednum = 32;1132	client->demux.start_feed = smsdvb_start_feed;1133	client->demux.stop_feed = smsdvb_stop_feed;1134 1135	rc = dvb_dmx_init(&client->demux);1136	if (rc < 0) {1137		pr_err("dvb_dmx_init failed %d\n", rc);1138		goto dvbdmx_error;1139	}1140 1141	/* init dmxdev */1142	client->dmxdev.filternum = 32;1143	client->dmxdev.demux = &client->demux.dmx;1144	client->dmxdev.capabilities = 0;1145 1146	rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);1147	if (rc < 0) {1148		pr_err("dvb_dmxdev_init failed %d\n", rc);1149		goto dmxdev_error;1150	}1151 1152	/* init and register frontend */1153	memcpy(&client->frontend.ops, &smsdvb_fe_ops,1154	       sizeof(struct dvb_frontend_ops));1155 1156	switch (smscore_get_device_mode(coredev)) {1157	case DEVICE_MODE_DVBT:1158	case DEVICE_MODE_DVBT_BDA:1159		client->frontend.ops.delsys[0] = SYS_DVBT;1160		break;1161	case DEVICE_MODE_ISDBT:1162	case DEVICE_MODE_ISDBT_BDA:1163		client->frontend.ops.delsys[0] = SYS_ISDBT;1164		break;1165	}1166 1167	rc = dvb_register_frontend(&client->adapter, &client->frontend);1168	if (rc < 0) {1169		pr_err("frontend registration failed %d\n", rc);1170		goto frontend_error;1171	}1172 1173	params.initial_id = 1;1174	params.data_type = MSG_SMS_DVBT_BDA_DATA;1175	params.onresponse_handler = smsdvb_onresponse;1176	params.onremove_handler = smsdvb_onremove;1177	params.context = client;1178 1179	rc = smscore_register_client(coredev, &params, &client->smsclient);1180	if (rc < 0) {1181		pr_err("smscore_register_client() failed %d\n", rc);1182		goto client_error;1183	}1184 1185	client->coredev = coredev;1186 1187	init_completion(&client->tune_done);1188	init_completion(&client->stats_done);1189 1190	mutex_lock(&g_smsdvb_clientslock);1191 1192	list_add(&client->entry, &g_smsdvb_clients);1193 1194	mutex_unlock(&g_smsdvb_clientslock);1195 1196	client->event_fe_state = -1;1197	client->event_unc_state = -1;1198	sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG);1199 1200	sms_board_setup(coredev);1201 1202	if (smsdvb_debugfs_create(client) < 0)1203		pr_info("failed to create debugfs node\n");1204 1205	rc = dvb_create_media_graph(&client->adapter, true);1206	if (rc < 0) {1207		pr_err("dvb_create_media_graph failed %d\n", rc);1208		goto media_graph_error;1209	}1210 1211	pr_info("DVB interface registered.\n");1212	return 0;1213 1214media_graph_error:1215	mutex_lock(&g_smsdvb_clientslock);1216	list_del(&client->entry);1217	mutex_unlock(&g_smsdvb_clientslock);1218 1219	smsdvb_debugfs_release(client);1220 1221client_error:1222	dvb_unregister_frontend(&client->frontend);1223 1224frontend_error:1225	dvb_dmxdev_release(&client->dmxdev);1226 1227dmxdev_error:1228	dvb_dmx_release(&client->demux);1229 1230dvbdmx_error:1231	smsdvb_media_device_unregister(client);1232	dvb_unregister_adapter(&client->adapter);1233 1234adapter_error:1235	kfree(client);1236	return rc;1237}1238 1239static int __init smsdvb_module_init(void)1240{1241	int rc;1242 1243	smsdvb_debugfs_register();1244 1245	rc = smscore_register_hotplug(smsdvb_hotplug);1246 1247	pr_debug("\n");1248 1249	return rc;1250}1251 1252static void __exit smsdvb_module_exit(void)1253{1254	smscore_unregister_hotplug(smsdvb_hotplug);1255 1256	mutex_lock(&g_smsdvb_clientslock);1257 1258	while (!list_empty(&g_smsdvb_clients))1259		smsdvb_unregister_client((struct smsdvb_client_t *)g_smsdvb_clients.next);1260 1261	smsdvb_debugfs_unregister();1262 1263	mutex_unlock(&g_smsdvb_clientslock);1264}1265 1266module_init(smsdvb_module_init);1267module_exit(smsdvb_module_exit);1268 1269MODULE_DESCRIPTION("SMS DVB subsystem adaptation module");1270MODULE_AUTHOR("Siano Mobile Silicon, Inc. <uris@siano-ms.com>");1271MODULE_LICENSE("GPL");1272