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