mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
284acd422d
PD#SWPL-134419 PD#SWPL-141381 Problem: fix coverity check. remove debug logs. Solution: fix coverity check. remove debug logs. AML_DVB_EXTERN_VERSION "V1.16". Verify: Oppen. Change-Id: I3fa79ba4c9283a590455b8242dee2d8e1158bee6 Signed-off-by: nengwen.chen <nengwen.chen@amlogic.com>
1655 lines
46 KiB
C
1655 lines
46 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/amlogic/aml_tuner.h>
|
|
#include <linux/amlogic/aml_dtvdemod.h>
|
|
#include <linux/amlogic/aml_dvb_extern.h>
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
#include <linux/compat.h>
|
|
#endif
|
|
|
|
#include "aml_dvb_extern_driver.h"
|
|
|
|
#define AML_DVB_EXTERN_DEVICE_NAME "aml_dvb_extern"
|
|
#define AML_DVB_EXTERN_DRIVER_NAME "aml_dvb_extern"
|
|
#define AML_DVB_EXTERN_MODULE_NAME "aml_dvb_extern"
|
|
#define AML_DVB_EXTERN_CLASS_NAME "aml_dvb_extern"
|
|
|
|
#define AML_DVB_EXTERN_VERSION "V1.16"
|
|
|
|
static struct dvb_extern_device *dvb_extern_dev;
|
|
static struct mutex dvb_extern_mutex;
|
|
|
|
|
|
static char *fe_type_name[] = {
|
|
"FE_QPSK",
|
|
"FE_QAM",
|
|
"FE_OFDM",
|
|
"FE_ATSC",
|
|
"FE_ANALOG",
|
|
"FE_DTMB",
|
|
"FE_ISDBT"
|
|
};
|
|
|
|
static char *fe_delsys_name[] = {
|
|
"SYS_UNDEFINED",
|
|
"SYS_DVBC_ANNEX_A",
|
|
"SYS_DVBC_ANNEX_B",
|
|
"SYS_DVBT",
|
|
"SYS_DSS",
|
|
"SYS_DVBS",
|
|
"SYS_DVBS2",
|
|
"SYS_DVBH",
|
|
"SYS_ISDBT",
|
|
"SYS_ISDBS",
|
|
"SYS_ISDBC",
|
|
"SYS_ATSC",
|
|
"SYS_ATSCMH",
|
|
"SYS_DTMB",
|
|
"SYS_CMMB",
|
|
"SYS_DAB",
|
|
"SYS_DVBT2",
|
|
"SYS_TURBO",
|
|
"SYS_DVBC_ANNEX_C",
|
|
"SYS_ANALOG"
|
|
};
|
|
|
|
static char *fe_modulation_name[] = {
|
|
"QPSK",
|
|
"QAM_16",
|
|
"QAM_32",
|
|
"QAM_64",
|
|
"QAM_128",
|
|
"QAM_256",
|
|
"QAM_AUTO",
|
|
"VSB_8",
|
|
"VSB_16",
|
|
"PSK_8",
|
|
"APSK_16",
|
|
"APSK_32",
|
|
"DQPSK",
|
|
"QAM_4_NR"
|
|
};
|
|
|
|
static void aml_dvb_extern_set_power(struct gpio_config *pin_cfg, int on)
|
|
{
|
|
if (!aml_gpio_is_valid(pin_cfg->pin)) {
|
|
pr_err("%s: dvb power gpio is invalid.", __func__);
|
|
|
|
return;
|
|
}
|
|
|
|
/* OD pin[No output capacity], set direction output as low output. */
|
|
if (pin_cfg->dir == GPIOF_DIR_OUT) {
|
|
if (on)
|
|
aml_gpio_set_value(pin_cfg->pin, pin_cfg->value);
|
|
else
|
|
aml_gpio_set_value(pin_cfg->pin, !(pin_cfg->value));
|
|
} else {
|
|
if (on) {
|
|
aml_gpio_direction_input(pin_cfg->pin);
|
|
aml_gpio_set_value(pin_cfg->pin, pin_cfg->value);
|
|
} else {
|
|
aml_gpio_direction_output(pin_cfg->pin, !(pin_cfg->value));
|
|
}
|
|
}
|
|
}
|
|
|
|
void aml_dvb_extern_resume_work(struct work_struct *work)
|
|
{
|
|
struct tuner_ops *tops = NULL;
|
|
struct dvb_tuner *tuner = get_dvb_tuners();
|
|
|
|
list_for_each_entry(tops, &tuner->list, list) {
|
|
if (tops->fe.ops.tuner_ops.resume)
|
|
tops->fe.ops.tuner_ops.resume(&tops->fe);
|
|
else if (tops->fe.ops.tuner_ops.init)
|
|
tops->fe.ops.tuner_ops.init(&tops->fe);
|
|
}
|
|
}
|
|
|
|
void aml_dvb_extern_attach_work(struct work_struct *work)
|
|
{
|
|
struct dvb_demod *demod = get_dvb_demods();
|
|
struct demod_ops *demod_ops = NULL;
|
|
struct aml_attach_work *p = NULL;
|
|
int cur_id = 0;
|
|
|
|
mutex_lock(&dvb_extern_mutex);
|
|
|
|
p = container_of(work, struct aml_attach_work, work);
|
|
cur_id = p->cur_id;
|
|
|
|
demod->attach(demod, true);
|
|
demod->detect(demod);
|
|
demod->register_frontend(demod, true);
|
|
demod->pre_init(demod);
|
|
|
|
if (cur_id >= dvb_extern_dev->demod_num) {
|
|
pr_err("demod index %d error, the max is %d, use default %d.\n",
|
|
cur_id, dvb_extern_dev->demod_num - 1,
|
|
dvb_extern_dev->demod_cur);
|
|
mutex_unlock(&dvb_extern_mutex);
|
|
return;
|
|
}
|
|
|
|
dvb_extern_dev->demod_cur = cur_id;
|
|
demod_ops = dvb_demod_ops_get_byindex(dvb_extern_dev->demod_cur);
|
|
if (demod_ops && demod_ops->fe) {
|
|
dvb_extern_dev->demod_fe = demod_ops->fe;
|
|
demod->used = demod_ops;
|
|
pr_err("%s demod %d attach done.\n", __func__,
|
|
dvb_extern_dev->demod_cur);
|
|
}
|
|
|
|
mutex_unlock(&dvb_extern_mutex);
|
|
}
|
|
|
|
void aml_dvb_extern_attach(void)
|
|
{
|
|
pr_err("[%s] ready to schedule work.\n", __func__);
|
|
|
|
mutex_lock(&dvb_extern_mutex);
|
|
if (IS_ERR_OR_NULL(dvb_extern_dev)) {
|
|
pr_err("[%s] error! dvb_extern_dev is null.\n", __func__);
|
|
mutex_unlock(&dvb_extern_mutex);
|
|
} else {
|
|
dvb_extern_dev->attach_work.cur_id = 0;
|
|
|
|
mutex_unlock(&dvb_extern_mutex);
|
|
|
|
schedule_work(&dvb_extern_dev->attach_work.work);
|
|
}
|
|
}
|
|
|
|
static ssize_t tuner_debug_store(struct class *class,
|
|
struct class_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
int n = 0, tuner_id = 0, fe_type = 0;
|
|
char *buf_orig = NULL, *ps = NULL, *token = NULL, *name = NULL;
|
|
char *parm[10] = { NULL };
|
|
unsigned long val = 0, freq = 0, symbol_rate = 0, bw = 0, modul = 0;
|
|
unsigned long rolloff = 0;
|
|
struct tuner_ops *ops = NULL;
|
|
struct dvb_tuner *tuner = get_dvb_tuners();
|
|
struct dvb_extern_device *dev =
|
|
container_of(class, struct dvb_extern_device, class);
|
|
struct dvb_frontend *fe = dev->tuner_fe;
|
|
struct analog_parameters *p = &dev->para;
|
|
struct dtv_frontend_properties *c = NULL;
|
|
u32 status = FE_TIMEDOUT, frequency = 0, bandwidth = 0;
|
|
u32 if_frequency[2] = { 0 };
|
|
s16 strength = 0;
|
|
|
|
buf_orig = kstrdup(buf, GFP_KERNEL);
|
|
ps = buf_orig;
|
|
|
|
while (1) {
|
|
token = strsep(&ps, "\n ");
|
|
if (!token)
|
|
break;
|
|
|
|
if (*token == '\0')
|
|
continue;
|
|
|
|
if (n >= 10)
|
|
break;
|
|
|
|
parm[n++] = token;
|
|
}
|
|
|
|
if (!parm[0] || !fe)
|
|
goto EXIT;
|
|
|
|
if (strncmp(parm[0], "attach", 6) && strncmp(parm[0], "status", 6)) {
|
|
pr_err("please attach first:\n");
|
|
pr_err("echo attach [tuner_id] > /sys/class/aml_dvb_extern/tuner_debug\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!strncmp(parm[0], "init", 4)) {
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &val))
|
|
fe->ops.info.type = val;
|
|
else
|
|
fe->ops.info.type = FE_ANALOG;
|
|
|
|
if (val > 6) {
|
|
pr_err("init tuner mode value [%ld] error.\n", val);
|
|
goto EXIT;
|
|
}
|
|
|
|
pr_err("init tuner mode [%d][%s].\n",
|
|
fe->ops.info.type,
|
|
fe_type_name[fe->ops.info.type]);
|
|
|
|
if (fe->ops.tuner_ops.init)
|
|
fe->ops.tuner_ops.init(fe);
|
|
|
|
if (fe->ops.tuner_ops.set_config)
|
|
fe->ops.tuner_ops.set_config(fe, NULL);
|
|
} else if (!strncmp(parm[0], "tune", 4)) {
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &freq)) {
|
|
p->frequency = freq;
|
|
} else {
|
|
pr_err("please input frequency.\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (fe->ops.info.type == FE_ANALOG) {
|
|
p->frequency = freq;
|
|
if (fe->ops.tuner_ops.set_analog_params)
|
|
fe->ops.tuner_ops.set_analog_params(fe, p);
|
|
|
|
pr_err("tune mode [%d][%s] freq[%d] std[0x%llx], audmode[0x%x].\n",
|
|
fe->ops.info.type,
|
|
fe_type_name[fe->ops.info.type],
|
|
p->frequency,
|
|
p->std, p->audmode);
|
|
} else {
|
|
c = &fe->dtv_property_cache;
|
|
|
|
c->frequency = freq;
|
|
|
|
if (fe->ops.info.type == FE_QPSK)
|
|
c->frequency = c->frequency / 1000;
|
|
|
|
if (parm[2] && !kstrtoul(parm[2], 0, &symbol_rate))
|
|
c->symbol_rate = symbol_rate;
|
|
else
|
|
c->symbol_rate = 0;
|
|
|
|
if (parm[3] && !kstrtoul(parm[3], 0, &bw))
|
|
c->bandwidth_hz = bw;
|
|
else
|
|
c->bandwidth_hz = 8000000;
|
|
|
|
if (parm[4] && !kstrtoul(parm[4], 0, &modul)) {
|
|
c->modulation = modul;
|
|
} else {
|
|
switch (fe->ops.info.type) {
|
|
case FE_QPSK:
|
|
c->modulation = QPSK;
|
|
break;
|
|
case FE_ATSC:
|
|
c->modulation = VSB_8;
|
|
break;
|
|
default:
|
|
c->modulation = QAM_AUTO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (parm[5] && !kstrtoul(parm[5], 0, &rolloff))
|
|
c->rolloff = rolloff;
|
|
else
|
|
c->rolloff = ROLLOFF_AUTO;
|
|
|
|
if (fe->ops.tuner_ops.set_params)
|
|
fe->ops.tuner_ops.set_params(fe);
|
|
|
|
pr_err("tune mode [%d][%s], delivery_system %d, frequency %d, symbol_rate %d, bandwidth_hz %d, modulation %d, rolloff %d, inversion %d.\n",
|
|
fe->ops.info.type,
|
|
fe_type_name[fe->ops.info.type],
|
|
c->delivery_system,
|
|
c->frequency, c->symbol_rate,
|
|
c->bandwidth_hz, c->modulation,
|
|
c->rolloff, c->inversion);
|
|
}
|
|
} else if (!strncmp(parm[0], "std", 3)) {
|
|
if (!strncmp(parm[1], "pal", 3))
|
|
p->std = V4L2_COLOR_STD_PAL;
|
|
else if (!strncmp(parm[1], "ntsc", 4))
|
|
p->std = V4L2_COLOR_STD_NTSC;
|
|
else if (!strncmp(parm[1], "secam", 5))
|
|
p->std = V4L2_COLOR_STD_SECAM;
|
|
else
|
|
pr_err("invalid video std: %s.\n", parm[1]);
|
|
|
|
if (!strncmp(parm[2], "dk", 2))
|
|
p->std |= V4L2_STD_PAL_DK;
|
|
else if (!strncmp(parm[2], "bg", 2))
|
|
p->std |= V4L2_STD_BG;
|
|
else if (!strncmp(parm[2], "i", 1))
|
|
p->std |= V4L2_STD_PAL_I;
|
|
else if (!strncmp(parm[2], "nm", 2))
|
|
p->std |= V4L2_STD_NTSC;
|
|
else if (!strncmp(parm[2], "pm", 2))
|
|
p->std |= V4L2_STD_PAL_M;
|
|
else if (!strncmp(parm[2], "lc", 2))
|
|
p->std |= V4L2_STD_SECAM_LC;
|
|
else if (!strncmp(parm[2], "l", 1))
|
|
p->std |= V4L2_STD_SECAM_L;
|
|
else if (!strncmp(parm[2], "n", 1))
|
|
p->std |= V4L2_STD_PAL_N;
|
|
else
|
|
pr_err("invalid audio std: %s.\n", parm[2]);
|
|
} else if (!strncmp(parm[0], "status", 6)) {
|
|
pr_err("tuner numbers: %d.\n", dev->tuner_num);
|
|
pr_err("all tuners:\n");
|
|
list_for_each_entry(ops, &tuner->list, list) {
|
|
name = ops->fe.ops.tuner_ops.info.name;
|
|
pr_err("tuner%d, id %d (%s) addr 0x%x, adap 0x%p %s, %sdetected.\n",
|
|
ops->index, ops->cfg.id,
|
|
name ? name : "",
|
|
ops->cfg.i2c_addr, ops->cfg.i2c_adap,
|
|
ops->attached ? "attached" : "detached",
|
|
ops->cfg.detect ? (ops->valid ? "" : "un") : "no ");
|
|
}
|
|
|
|
if (tuner->used) {
|
|
name = tuner->used->fe.ops.tuner_ops.info.name;
|
|
pr_err("current use tuner%d, id %d (%s), fe type %d.\n",
|
|
tuner->used->index,
|
|
tuner->used->cfg.id,
|
|
name ? name : "",
|
|
tuner->used->type);
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_status) {
|
|
tuner->used->fe.ops.tuner_ops.get_status(
|
|
&tuner->used->fe, &status);
|
|
pr_err("signal status: %s.\n",
|
|
status & FE_HAS_LOCK ? "Locked" : "Unlocked");
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_strength) {
|
|
tuner->used->fe.ops.tuner_ops.get_strength(
|
|
&tuner->used->fe, &strength);
|
|
pr_err("strength: %d dBm.\n", strength);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_frequency) {
|
|
tuner->used->fe.ops.tuner_ops.get_frequency(
|
|
&tuner->used->fe, &frequency);
|
|
pr_err("frequency: %d Hz.\n", frequency);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_bandwidth) {
|
|
tuner->used->fe.ops.tuner_ops.get_bandwidth(
|
|
&tuner->used->fe, &bandwidth);
|
|
pr_err("bandwidth: %d Hz.\n", bandwidth);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_if_frequency) {
|
|
tuner->used->fe.ops.tuner_ops.get_if_frequency(
|
|
&tuner->used->fe, if_frequency);
|
|
pr_err("if frequency: %d Hz (IF spectrum %s).\n",
|
|
if_frequency[1],
|
|
if_frequency[0] ? "inverted" : "normal");
|
|
}
|
|
}
|
|
} else if (!strncmp(parm[0], "uninit", 6)) {
|
|
if (fe->ops.tuner_ops.release)
|
|
fe->ops.tuner_ops.release(fe);
|
|
} else if (!strncmp(parm[0], "attach", 6)) {
|
|
tuner->attach(tuner, true);
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &val))
|
|
tuner_id = val;
|
|
else
|
|
tuner_id = 0;
|
|
|
|
if (tuner_id >= dev->tuner_num) {
|
|
pr_err("input tuner index %d error, the max is %d, use default %d.\n",
|
|
tuner_id, dev->tuner_num - 1,
|
|
dev->tuner_cur);
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
dev->tuner_cur = tuner_id;
|
|
ops = dvb_tuner_ops_get_byindex(dev->tuner_cur);
|
|
if (ops) {
|
|
dev->tuner_fe = &ops->fe;
|
|
//memcpy(&fe->ops.tuner_ops, &ops->fe.ops.tuner_ops,
|
|
// sizeof(struct dvb_tuner_ops));
|
|
//fe->tuner_priv = ops->fe.tuner_priv;
|
|
|
|
pr_err("tuner %d attach done.\n", dev->tuner_cur);
|
|
}
|
|
} else if (!strncmp(parm[0], "detach", 6)) {
|
|
tuner->attach(tuner, false);
|
|
} else if (!strncmp(parm[0], "match", 5)) {
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &val))
|
|
fe_type = val;
|
|
else
|
|
fe_type = FE_ANALOG;
|
|
|
|
if (fe_type > FE_ISDBT) {
|
|
pr_err("input tuner match fe type %d error.\n",
|
|
fe_type);
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
if (tuner->match(tuner, fe_type))
|
|
pr_err("tuner match fe type %d (%s) done.\n",
|
|
fe_type, fe_type_name[fe_type]);
|
|
else
|
|
pr_err("tuner match fe type %d (%s) fail.\n",
|
|
fe_type, fe_type_name[fe_type]);
|
|
|
|
} else if (!strncmp(parm[0], "detect", 6)) {
|
|
tuner->detect(tuner);
|
|
|
|
dev->tuner_fe = NULL;
|
|
} else {
|
|
pr_err("invalid command: %s.\n", parm[0]);
|
|
}
|
|
|
|
EXIT:
|
|
kfree(buf_orig);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t tuner_debug_show(struct class *class,
|
|
struct class_attribute *attr, char *buff)
|
|
{
|
|
int n = 0;
|
|
char *name = NULL;
|
|
const char *path = "/sys/class/aml_dvb_extern/tuner_debug";
|
|
struct tuner_ops *ops = NULL;
|
|
struct dvb_tuner *tuner = get_dvb_tuners();
|
|
struct dvb_extern_device *dev =
|
|
container_of(class, struct dvb_extern_device, class);
|
|
u32 status = FE_TIMEDOUT, frequency = 0, bandwidth = 0;
|
|
u32 if_frequency[2] = { 0 };
|
|
s16 strength = 0;
|
|
|
|
n += sprintf(buff + n, "\nTuner Debug Usage (%s):\n", AML_DVB_EXTERN_VERSION);
|
|
n += sprintf(buff + n, "[status]\necho status > %s\n", path);
|
|
n += sprintf(buff + n, "[attach]\necho attach [tuner_id] > %s\n", path);
|
|
n += sprintf(buff + n, "[detach]\necho detach > %s\n", path);
|
|
n += sprintf(buff + n, "[match]\necho match [fe_type] > %s\n", path);
|
|
n += sprintf(buff + n, "\tfe_type:\n");
|
|
n += sprintf(buff + n, "\tDVB-S/S2 [FE_QPSK] : 0\n");
|
|
n += sprintf(buff + n, "\tDVB-C [FE_QAM] : 1\n");
|
|
n += sprintf(buff + n, "\tDVB-T/T2 [FE_OFDM] : 2\n");
|
|
n += sprintf(buff + n, "\tATSC [FE_ATSC] : 3\n");
|
|
n += sprintf(buff + n, "\tANALOG [FE_ANALOG]: 4\n");
|
|
n += sprintf(buff + n, "\tDTMB [FE_DTMB] : 5\n");
|
|
n += sprintf(buff + n, "\tISDBT [FE_ISDBT] : 6\n");
|
|
n += sprintf(buff + n, "[detect]\necho detect > %s\n", path);
|
|
n += sprintf(buff + n, "[init]\n");
|
|
n += sprintf(buff + n, "echo init [fe_type] > %s\n", path);
|
|
n += sprintf(buff + n, "\tfe_type:\n");
|
|
n += sprintf(buff + n, "\tDVB-S/S2 [FE_QPSK] : 0\n");
|
|
n += sprintf(buff + n, "\tDVB-C [FE_QAM] : 1\n");
|
|
n += sprintf(buff + n, "\tDVB-T/T2 [FE_OFDM] : 2\n");
|
|
n += sprintf(buff + n, "\tATSC [FE_ATSC] : 3\n");
|
|
n += sprintf(buff + n, "\tANALOG [FE_ANALOG]: 4\n");
|
|
n += sprintf(buff + n, "\tDTMB [FE_DTMB] : 5\n");
|
|
n += sprintf(buff + n, "\tISDBT [FE_ISDBT] : 6\n");
|
|
n += sprintf(buff + n, "[set atv std]\n");
|
|
n += sprintf(buff + n, "echo std [vstd] [astd] > %s\n", path);
|
|
n += sprintf(buff + n, "\tvstd: pal, ntsc, secam\n");
|
|
n += sprintf(buff + n, "\tastd: dk, bg, i, nm, pm, l, lc, n\n");
|
|
n += sprintf(buff + n, "[tune]\n");
|
|
n += sprintf(buff + n, "echo tune [frequency_hz] [symbol_rate_bps] ");
|
|
n += sprintf(buff + n, "[bw_hz] [modulation] [rolloff]");
|
|
n += sprintf(buff + n, " > %s\n", path);
|
|
n += sprintf(buff + n, "\tmodulation:\n");
|
|
n += sprintf(buff + n, "\tQPSK : 0\n");
|
|
n += sprintf(buff + n, "\tQAM_16 : 1\n");
|
|
n += sprintf(buff + n, "\tQAM_32 : 2\n");
|
|
n += sprintf(buff + n, "\tQAM_64 : 3\n");
|
|
n += sprintf(buff + n, "\tQAM_128 : 4\n");
|
|
n += sprintf(buff + n, "\tQAM_256 : 5\n");
|
|
n += sprintf(buff + n, "\tQAM_AUTO : 6\n");
|
|
n += sprintf(buff + n, "\tVSB_8 : 7\n");
|
|
n += sprintf(buff + n, "\tVSB_16 : 8\n");
|
|
n += sprintf(buff + n, "\tPSK_8 : 9\n");
|
|
n += sprintf(buff + n, "\tAPSK_16 : 10\n");
|
|
n += sprintf(buff + n, "\tAPSK_32 : 11\n");
|
|
n += sprintf(buff + n, "\tDQPSK : 12\n");
|
|
n += sprintf(buff + n, "\tQAM_4_NR : 13\n");
|
|
n += sprintf(buff + n, "\trolloff:\n");
|
|
n += sprintf(buff + n, "\tROLLOFF_35 : 0\n");
|
|
n += sprintf(buff + n, "\tROLLOFF_20 : 1\n");
|
|
n += sprintf(buff + n, "\tROLLOFF_25 : 2\n");
|
|
n += sprintf(buff + n, "\tROLLOFF_AUTO : 3\n");
|
|
n += sprintf(buff + n, "[uninit]\necho uninit > %s\n", path);
|
|
|
|
n += sprintf(buff + n, "\n------------------------------------------------------------\n");
|
|
n += sprintf(buff + n, "\nTuner Status:\n");
|
|
n += sprintf(buff + n, "tuner numbers: %d.\n", dev->tuner_num);
|
|
n += sprintf(buff + n, "all tuners:\n");
|
|
list_for_each_entry(ops, &tuner->list, list) {
|
|
name = ops->fe.ops.tuner_ops.info.name;
|
|
n += sprintf(buff + n, "tuner%d, id %d (%s) addr 0x%x, adap 0x%p %s, %sdetected.\n",
|
|
ops->index, ops->cfg.id,
|
|
name ? name : "",
|
|
ops->cfg.i2c_addr, ops->cfg.i2c_adap,
|
|
ops->attached ? "attached" : "detached",
|
|
ops->cfg.detect ? (ops->valid ? "" : "un") : "no ");
|
|
}
|
|
|
|
if (tuner->used) {
|
|
name = tuner->used->fe.ops.tuner_ops.info.name;
|
|
n += sprintf(buff + n, "current use tuner%d, id %d (%s), fe type %d.\n",
|
|
tuner->used->index,
|
|
tuner->used->cfg.id,
|
|
name ? name : "",
|
|
tuner->used->type);
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_status) {
|
|
tuner->used->fe.ops.tuner_ops.get_status(
|
|
&tuner->used->fe, &status);
|
|
n += sprintf(buff + n, "signal status: %s.\n",
|
|
status & FE_HAS_LOCK ? "Locked" : "Unlocked");
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_strength) {
|
|
tuner->used->fe.ops.tuner_ops.get_strength(
|
|
&tuner->used->fe, &strength);
|
|
n += sprintf(buff + n, "strength: %d dBm.\n", strength);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_frequency) {
|
|
tuner->used->fe.ops.tuner_ops.get_frequency(
|
|
&tuner->used->fe, &frequency);
|
|
n += sprintf(buff + n, "frequency: %d Hz.\n", frequency);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_bandwidth) {
|
|
tuner->used->fe.ops.tuner_ops.get_bandwidth(
|
|
&tuner->used->fe, &bandwidth);
|
|
n += sprintf(buff + n, "bandwidth: %d Hz.\n", bandwidth);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_if_frequency) {
|
|
tuner->used->fe.ops.tuner_ops.get_if_frequency(
|
|
&tuner->used->fe, if_frequency);
|
|
n += sprintf(buff + n, "if frequency: %d Hz (IF spectrum %s).\n",
|
|
if_frequency[1],
|
|
if_frequency[0] ? "inverted" : "normal");
|
|
}
|
|
}
|
|
|
|
n += sprintf(buff + n, "\n");
|
|
|
|
return n;
|
|
}
|
|
|
|
static ssize_t demod_debug_store(struct class *class,
|
|
struct class_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
int n = 0, fe_type = 0, ret = 0;
|
|
unsigned int delay = 0;
|
|
char *buf_orig = NULL, *ps = NULL, *token = NULL, *name = NULL;
|
|
char *parm[10] = { NULL };
|
|
unsigned long val = 0, freq = 0, symbol_rate = 0, bw = 0, modul = 0;
|
|
unsigned long rolloff = 0;
|
|
struct demod_ops *ops = NULL;
|
|
struct dvb_demod *demod = get_dvb_demods();
|
|
struct dvb_extern_device *dev =
|
|
container_of(class, struct dvb_extern_device, class);
|
|
struct dvb_frontend *fe = dev->demod_fe;
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
struct dtv_property tvp;
|
|
enum fe_status s = FE_NONE;
|
|
enum fe_delivery_system delsys = 0;
|
|
|
|
buf_orig = kstrdup(buf, GFP_KERNEL);
|
|
ps = buf_orig;
|
|
|
|
while (1) {
|
|
token = strsep(&ps, "\n ");
|
|
if (!token)
|
|
break;
|
|
|
|
if (*token == '\0')
|
|
continue;
|
|
|
|
if (n >= 10)
|
|
break;
|
|
|
|
parm[n++] = token;
|
|
}
|
|
|
|
if (!parm[0] || !fe)
|
|
goto EXIT;
|
|
|
|
if (strncmp(parm[0], "attach", 6) && strncmp(parm[0], "status", 6)) {
|
|
pr_err("please attach first:\n");
|
|
pr_err("echo attach [demod_id] > /sys/class/aml_dvb_extern/demod_debug\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!strncmp(parm[0], "init", 4)) {
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &val))
|
|
delsys = val;
|
|
else
|
|
delsys = SYS_DTMB;
|
|
|
|
if (delsys > SYS_ANALOG) {
|
|
pr_err("init demod delsys value [%d] error.\n",
|
|
delsys);
|
|
goto EXIT;
|
|
}
|
|
|
|
memset(&tvp, 0, sizeof(tvp));
|
|
c->delivery_system = delsys;
|
|
tvp.cmd = DTV_DELIVERY_SYSTEM;
|
|
tvp.u.data = c->delivery_system;
|
|
|
|
pr_err("init demod delsys [%d][%s].\n",
|
|
c->delivery_system,
|
|
fe_delsys_name[c->delivery_system]);
|
|
|
|
if (fe->ops.init)
|
|
fe->ops.init(fe);
|
|
|
|
if (fe->ops.set_property)
|
|
fe->ops.set_property(fe, &tvp);
|
|
} else if (!strncmp(parm[0], "tune", 4)) {
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &val))
|
|
freq = val;
|
|
else
|
|
freq = 88000000;
|
|
|
|
if (parm[2] && !kstrtoul(parm[2], 0, &val))
|
|
symbol_rate = val;
|
|
else
|
|
symbol_rate = 0;
|
|
|
|
if (parm[3] && !kstrtoul(parm[3], 0, &val))
|
|
bw = val;
|
|
else
|
|
bw = 8000000;
|
|
|
|
if (parm[4] && !kstrtoul(parm[4], 0, &val)) {
|
|
modul = val;
|
|
} else {
|
|
switch (c->delivery_system) {
|
|
case SYS_DVBS:
|
|
case SYS_DVBS2:
|
|
case SYS_TURBO:
|
|
modul = QPSK;
|
|
break;
|
|
case SYS_ATSC:
|
|
modul = VSB_8;
|
|
break;
|
|
default:
|
|
modul = QAM_AUTO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (parm[5] && !kstrtoul(parm[5], 0, &val))
|
|
rolloff = val;
|
|
else
|
|
rolloff = ROLLOFF_AUTO;
|
|
|
|
if (c->delivery_system == SYS_DVBS || c->delivery_system == SYS_DVBS2)
|
|
c->frequency = freq / 1000;
|
|
else
|
|
c->frequency = freq;
|
|
|
|
c->symbol_rate = symbol_rate;
|
|
c->bandwidth_hz = bw;
|
|
c->modulation = modul;
|
|
c->rolloff = rolloff;
|
|
|
|
pr_err("tune delivery_system %d, frequency %d, symbol_rate %d, bandwidth_hz %d, modulation %d, rolloff %d, inversion %d.\n",
|
|
c->delivery_system, c->frequency, c->symbol_rate,
|
|
c->bandwidth_hz, c->modulation,
|
|
c->rolloff, c->inversion);
|
|
|
|
if (fe->ops.tune) {
|
|
fe->ops.tune(fe, true, 0, &delay, &s);
|
|
|
|
pr_err("tune status: [0x%x] %s.\n", s,
|
|
s & FE_HAS_LOCK ?
|
|
"Locked" : "Unlocked");
|
|
} else if (fe->ops.set_frontend) {
|
|
ret = fe->ops.set_frontend(fe);
|
|
|
|
pr_err("tune status: [0x%x] %s.\n", ret,
|
|
!ret ? "Locked" : "Unlocked");
|
|
}
|
|
|
|
} else if (!strncmp(parm[0], "status", 6)) {
|
|
pr_err("demod numbers: %d.\n", dev->demod_num);
|
|
pr_err("all demods:\n");
|
|
list_for_each_entry(ops, &demod->list, list) {
|
|
name = ops->fe ? ops->fe->ops.info.name : "";
|
|
pr_err("demod%d, id %d (%s) %s, %sregistered, %sdetected, use tuner0 %s, tuner1 %s.\n",
|
|
ops->index, ops->cfg.id,
|
|
name ? name : "", ops->attached ?
|
|
"attached" : "detached",
|
|
ops->registered ? "" : "un",
|
|
ops->cfg.detect ? (ops->valid ? "" : "un") : "no ",
|
|
ops->cfg.tuner0.name ? ops->cfg.tuner0.name : "NONE",
|
|
ops->cfg.tuner1.name ? ops->cfg.tuner1.name : "NONE");
|
|
}
|
|
|
|
if (demod->used && demod->used->fe) {
|
|
name = ops->fe->ops.info.name;
|
|
pr_err("current use demod%d, id %d (%s), fe type %d, delivery system %d.\n",
|
|
demod->used->index,
|
|
demod->used->cfg.id,
|
|
name ? name : "",
|
|
demod->used->type,
|
|
demod->used->fe->dtv_property_cache.delivery_system);
|
|
}
|
|
} else if (!strncmp(parm[0], "uninit", 6)) {
|
|
memset(&tvp, 0, sizeof(tvp));
|
|
c->delivery_system = SYS_ANALOG;
|
|
tvp.cmd = DTV_DELIVERY_SYSTEM;
|
|
tvp.u.data = c->delivery_system;
|
|
|
|
if (fe->ops.set_property)
|
|
fe->ops.set_property(fe, &tvp);
|
|
|
|
if (fe->ops.sleep)
|
|
fe->ops.sleep(fe);
|
|
} else if (!strncmp(parm[0], "attach", 6)) {
|
|
mutex_lock(&dvb_extern_mutex);
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &val))
|
|
dvb_extern_dev->attach_work.cur_id = val;
|
|
else
|
|
dvb_extern_dev->attach_work.cur_id = 0;
|
|
mutex_unlock(&dvb_extern_mutex);
|
|
schedule_work(&dvb_extern_dev->attach_work.work);
|
|
} else if (!strncmp(parm[0], "detach", 6)) {
|
|
demod->attach(demod, false);
|
|
|
|
dev->demod_fe = NULL;
|
|
} else if (!strncmp(parm[0], "match", 5)) {
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &val))
|
|
fe_type = val;
|
|
else
|
|
fe_type = FE_ANALOG;
|
|
|
|
if (fe_type > FE_ISDBT) {
|
|
pr_err("input demod match fe type %d error.\n",
|
|
fe_type);
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
if (demod->match(demod, fe_type))
|
|
pr_err("demod match fe type %d (%s) done.\n",
|
|
fe_type, fe_type_name[fe_type]);
|
|
else
|
|
pr_err("demod match fe type %d (%s) fail.\n",
|
|
fe_type, fe_type_name[fe_type]);
|
|
|
|
} else if (!strncmp(parm[0], "detect", 6)) {
|
|
demod->detect(demod);
|
|
} else if (!strncmp(parm[0], "register", 8)) {
|
|
demod->register_frontend(demod, true);
|
|
} else if (!strncmp(parm[0], "unregister", 10)) {
|
|
demod->register_frontend(demod, false);
|
|
} else if (!strncmp(parm[0], "getprop", 7)) { //ioctl cmd:FE_GET_PROPERTY
|
|
if (!strncmp(parm[1], "enum_delsys", 11)) { //prop cmd:DTV_ENUM_DELSYS
|
|
n = 0;
|
|
while (n < MAX_DELSYS && fe->ops.delsys[n]) {
|
|
pr_err("%s\n", fe_delsys_name[fe->ops.delsys[n]]);
|
|
n++;
|
|
}
|
|
} else if (!strncmp(parm[1], "ts_input", 8)) { //prop cmd: DTV_TS_INPUT
|
|
memset(&tvp, 0, sizeof(tvp));
|
|
tvp.cmd = DTV_TS_INPUT;
|
|
|
|
if (fe->ops.get_property) {
|
|
ret = fe->ops.get_property(fe, &tvp);
|
|
if (ret < 0)
|
|
pr_err("get_property failed.\n");
|
|
else
|
|
pr_err("ts in port: %d\n", tvp.u.data);
|
|
}
|
|
} else if (!strncmp(parm[1], "freq", 4)) { //prop cmd: DTV_FREQUENCY
|
|
pr_err("frequency_hz: %d\n", c->frequency);
|
|
} else if (!strncmp(parm[1], "mod", 3)) { //prop cmd: DTV_MODULATION
|
|
pr_err("modulation: %s\n", fe_modulation_name[c->modulation]);
|
|
} else if (!strncmp(parm[1], "bw", 2)) { //prop cmd: DTV_BANDWIDTH_HZ
|
|
pr_err("bandwidth_hz: %d\n", c->bandwidth_hz);
|
|
} else if (!strncmp(parm[1], "symbol_rate", 11)) { //prop cmd: DTV_SYMBOL_RATE
|
|
pr_err("symbol_rate: %d\n", c->symbol_rate);
|
|
} else if (!strncmp(parm[1], "delsys", 6)) { //prop cmd: DTV_DELIVERY_SYSTEM
|
|
pr_err("delsys: %s\n", fe_delsys_name[c->delivery_system]);
|
|
} else {
|
|
pr_err("invalid prop cmd: %s.\n", parm[1]);
|
|
}
|
|
} else {
|
|
pr_err("invalid command: %s.\n", parm[0]);
|
|
}
|
|
|
|
EXIT:
|
|
kfree(buf_orig);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t demod_debug_show(struct class *class,
|
|
struct class_attribute *attr, char *buff)
|
|
{
|
|
int n = 0;
|
|
char *name = NULL;
|
|
const char *path = "/sys/class/aml_dvb_extern/demod_debug";
|
|
struct demod_ops *ops = NULL;
|
|
struct dvb_demod *demod = get_dvb_demods();
|
|
struct dvb_extern_device *dev =
|
|
container_of(class, struct dvb_extern_device, class);
|
|
|
|
n += sprintf(buff + n, "\nDemod Debug Usage (%s):\n", AML_DVB_EXTERN_VERSION);
|
|
n += sprintf(buff + n, "[status]\necho status > %s\n", path);
|
|
n += sprintf(buff + n, "[attach]\necho attach [demod_id] > %s\n", path);
|
|
n += sprintf(buff + n, "[detach]\necho detach > %s\n", path);
|
|
n += sprintf(buff + n, "[match]\necho match [fe_type] > %s\n", path);
|
|
n += sprintf(buff + n, "\tfe_type:\n");
|
|
n += sprintf(buff + n, "\tDVB-S/S2 [FE_QPSK] : 0\n");
|
|
n += sprintf(buff + n, "\tDVB-C [FE_QAM] : 1\n");
|
|
n += sprintf(buff + n, "\tDVB-T/T2 [FE_OFDM] : 2\n");
|
|
n += sprintf(buff + n, "\tATSC [FE_ATSC] : 3\n");
|
|
n += sprintf(buff + n, "\tANALOG [FE_ANALOG]: 4\n");
|
|
n += sprintf(buff + n, "\tDTMB [FE_DTMB] : 5\n");
|
|
n += sprintf(buff + n, "\tISDBT [FE_ISDBT] : 6\n");
|
|
n += sprintf(buff + n, "[detect]\necho detect > %s\n", path);
|
|
n += sprintf(buff + n, "[register]\necho register > %s\n", path);
|
|
n += sprintf(buff + n, "[unregister]\necho unregister > %s\n", path);
|
|
n += sprintf(buff + n, "[init]\n");
|
|
n += sprintf(buff + n, "echo init [deliver_system] > %s\n", path);
|
|
n += sprintf(buff + n, "\tdeliver_system:\n");
|
|
n += sprintf(buff + n, "\tUNDEFINED : 0\n");
|
|
n += sprintf(buff + n, "\tDVBC_ANNEX_A : 1\n");
|
|
n += sprintf(buff + n, "\tDVBC_ANNEX_B : 2\n");
|
|
n += sprintf(buff + n, "\tDVBT : 3\n");
|
|
n += sprintf(buff + n, "\tDSS : 4\n");
|
|
n += sprintf(buff + n, "\tDVBS : 5\n");
|
|
n += sprintf(buff + n, "\tDVBS2 : 6\n");
|
|
n += sprintf(buff + n, "\tDVBH : 7\n");
|
|
n += sprintf(buff + n, "\tISDBT : 8\n");
|
|
n += sprintf(buff + n, "\tISDBS : 9\n");
|
|
n += sprintf(buff + n, "\tISDBC : 10\n");
|
|
n += sprintf(buff + n, "\tATSC : 11\n");
|
|
n += sprintf(buff + n, "\tATSCMH : 12\n");
|
|
n += sprintf(buff + n, "\tDTMB : 13\n");
|
|
n += sprintf(buff + n, "\tCMMB : 14\n");
|
|
n += sprintf(buff + n, "\tDAB : 15\n");
|
|
n += sprintf(buff + n, "\tDVBT2 : 16\n");
|
|
n += sprintf(buff + n, "\tTURBO : 17\n");
|
|
n += sprintf(buff + n, "\tDVBC_ANNEX_C : 18\n");
|
|
n += sprintf(buff + n, "\tANALOG : 19\n");
|
|
n += sprintf(buff + n, "[tune]\n");
|
|
n += sprintf(buff + n, "echo tune [frequency_hz] [symbol_rate_bps] ");
|
|
n += sprintf(buff + n, "[bw_hz] [modulation] [rolloff]");
|
|
n += sprintf(buff + n, " > %s\n", path);
|
|
n += sprintf(buff + n, "\tmodulation:\n");
|
|
n += sprintf(buff + n, "\tQPSK : 0\n");
|
|
n += sprintf(buff + n, "\tQAM_16 : 1\n");
|
|
n += sprintf(buff + n, "\tQAM_32 : 2\n");
|
|
n += sprintf(buff + n, "\tQAM_64 : 3\n");
|
|
n += sprintf(buff + n, "\tQAM_128 : 4\n");
|
|
n += sprintf(buff + n, "\tQAM_256 : 5\n");
|
|
n += sprintf(buff + n, "\tQAM_AUTO : 6\n");
|
|
n += sprintf(buff + n, "\tVSB_8 : 7\n");
|
|
n += sprintf(buff + n, "\tVSB_16 : 8\n");
|
|
n += sprintf(buff + n, "\tPSK_8 : 9\n");
|
|
n += sprintf(buff + n, "\tAPSK_16 : 10\n");
|
|
n += sprintf(buff + n, "\tAPSK_32 : 11\n");
|
|
n += sprintf(buff + n, "\tDQPSK : 12\n");
|
|
n += sprintf(buff + n, "\tQAM_4_NR : 13\n");
|
|
n += sprintf(buff + n, "\trolloff:\n");
|
|
n += sprintf(buff + n, "\tROLLOFF_35 : 0\n");
|
|
n += sprintf(buff + n, "\tROLLOFF_20 : 1\n");
|
|
n += sprintf(buff + n, "\tROLLOFF_25 : 2\n");
|
|
n += sprintf(buff + n, "\tROLLOFF_AUTO : 3\n");
|
|
n += sprintf(buff + n, "[uninit]\necho uninit > %s\n", path);
|
|
n += sprintf(buff + n, "[getprop]\n");
|
|
n += sprintf(buff + n, "echo getprop [prop_cmd] > %s\n", path);
|
|
n += sprintf(buff + n,
|
|
"\tprop_cmd: enum_delsys, ts_input, freq, mod, bw, symbol_rate, delsys\n");
|
|
|
|
n += sprintf(buff + n, "\n------------------------------------------------------------\n");
|
|
n += sprintf(buff + n, "\nDemod Status:\n");
|
|
n += sprintf(buff + n, "demod numbers: %d.\n", dev->demod_num);
|
|
n += sprintf(buff + n, "all demods:\n");
|
|
list_for_each_entry(ops, &demod->list, list) {
|
|
name = ops->fe ? ops->fe->ops.info.name : "";
|
|
n += sprintf(buff + n, "demod%d, id %d (%s) %s, %sregistered, %sdetected, use tuner0 %s, tuner1 %s.\n",
|
|
ops->index, ops->cfg.id,
|
|
name ? name : "", ops->attached ?
|
|
"attached" : "detached",
|
|
ops->registered ? "" : "un",
|
|
ops->cfg.detect ? (ops->valid ? "" : "un") : "no ",
|
|
ops->cfg.tuner0.name ? ops->cfg.tuner0.name : "NONE",
|
|
ops->cfg.tuner1.name ? ops->cfg.tuner1.name : "NONE");
|
|
}
|
|
|
|
if (demod->used && demod->used->fe) {
|
|
name = demod->used->fe->ops.info.name;
|
|
n += sprintf(buff + n,
|
|
"current use demod%d, id %d (%s), fe type %d, delivery system %d.\n",
|
|
demod->used->index,
|
|
demod->used->cfg.id,
|
|
name ? name : "",
|
|
demod->used->type,
|
|
demod->used->fe->dtv_property_cache.delivery_system);
|
|
}
|
|
|
|
n += sprintf(buff + n, "\n");
|
|
|
|
return n;
|
|
}
|
|
|
|
static ssize_t dvb_debug_store(struct class *class,
|
|
struct class_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
int n = 0;
|
|
unsigned long val = 0;
|
|
char *buf_orig = NULL, *ps = NULL, *token = NULL;
|
|
char *parm[10] = { NULL };
|
|
struct dvb_extern_device *dev =
|
|
container_of(class, struct dvb_extern_device, class);
|
|
|
|
buf_orig = kstrdup(buf, GFP_KERNEL);
|
|
ps = buf_orig;
|
|
|
|
while (1) {
|
|
token = strsep(&ps, "\n ");
|
|
if (!token)
|
|
break;
|
|
|
|
if (*token == '\0')
|
|
continue;
|
|
|
|
if (n >= 10)
|
|
break;
|
|
|
|
parm[n++] = token;
|
|
}
|
|
|
|
if (!parm[0])
|
|
goto EXIT;
|
|
|
|
if (!strncmp(parm[0], "dvb_power", 9)) {
|
|
if (parm[1] && !kstrtoul(parm[1], 0, &val)) {
|
|
aml_dvb_extern_set_power(&dev->dvb_power, val);
|
|
|
|
pr_err("set dvb power: %s.\n", val ? "ON" : "OFF");
|
|
} else {
|
|
pr_err("please input power status of DVB to be set: 1[ON] or 0[OFF].\n");
|
|
}
|
|
} else {
|
|
pr_err("invalid command: %s.\n", parm[0]);
|
|
}
|
|
|
|
EXIT:
|
|
kfree(buf_orig);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t dvb_debug_show(struct class *class,
|
|
struct class_attribute *attr, char *buff)
|
|
{
|
|
int n = 0;
|
|
const char *path = "/sys/class/aml_dvb_extern/dvb_debug";
|
|
struct dvb_extern_device *dev =
|
|
container_of(class, struct dvb_extern_device, class);
|
|
|
|
n += sprintf(buff + n, "\nDVB Extern Version: %s.\n",
|
|
AML_DVB_EXTERN_VERSION);
|
|
n += sprintf(buff + n, "\ndvb power gpio: %d, dir: %d, value: %d.\n",
|
|
dev->dvb_power.pin, dev->dvb_power.dir, dev->dvb_power.value);
|
|
n += sprintf(buff + n, "[set dvb power]\necho dvb_power [value] > %s\n",
|
|
path);
|
|
n += sprintf(buff + n, "\tvalue: 1 - [ON], 0 - [OFF].\n");
|
|
|
|
return n;
|
|
}
|
|
|
|
static int tuner_debug_seq_show(struct seq_file *m, void *v)
|
|
{
|
|
char *name = NULL;
|
|
struct tuner_ops *ops = NULL;
|
|
struct dvb_tuner *tuner = get_dvb_tuners();
|
|
u32 status = FE_TIMEDOUT, frequency = 0, bandwidth = 0;
|
|
u32 if_frequency[2] = { 0 };
|
|
s16 strength = 0;
|
|
struct dvb_extern_device *dev = m->private;
|
|
|
|
seq_puts(m, "\n------------------------------------------------------------\n");
|
|
seq_puts(m, "Tuner Status:\n");
|
|
seq_printf(m, "tuner numbers: %d.\n", dev->tuner_num);
|
|
seq_puts(m, "all tuners:\n");
|
|
list_for_each_entry(ops, &tuner->list, list) {
|
|
name = ops->fe.ops.tuner_ops.info.name;
|
|
seq_printf(m, "tuner %d, id %d (%s) addr 0x%x, adap 0x%p %s, %sdetected.\n",
|
|
ops->index, ops->cfg.id,
|
|
name ? name : "",
|
|
ops->cfg.i2c_addr, ops->cfg.i2c_adap,
|
|
ops->attached ? "attached" : "detached",
|
|
ops->cfg.detect ? (ops->valid ? "" : "un") : "no ");
|
|
}
|
|
|
|
if (tuner->used) {
|
|
name = tuner->used->fe.ops.tuner_ops.info.name;
|
|
seq_printf(m, "current use tuner%d, id %d (%s), fe type %d.\n",
|
|
tuner->used->index,
|
|
tuner->used->cfg.id,
|
|
name ? name : "",
|
|
tuner->used->type);
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_status) {
|
|
tuner->used->fe.ops.tuner_ops.get_status(
|
|
&tuner->used->fe, &status);
|
|
seq_printf(m, "signal status: %s.\n",
|
|
status & FE_HAS_LOCK ? "Locked" : "Unlocked");
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_strength) {
|
|
tuner->used->fe.ops.tuner_ops.get_strength(
|
|
&tuner->used->fe, &strength);
|
|
seq_printf(m, "strength: %d dBm.\n", strength);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_frequency) {
|
|
tuner->used->fe.ops.tuner_ops.get_frequency(
|
|
&tuner->used->fe, &frequency);
|
|
seq_printf(m, "frequency: %d Hz.\n", frequency);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_bandwidth) {
|
|
tuner->used->fe.ops.tuner_ops.get_bandwidth(
|
|
&tuner->used->fe, &bandwidth);
|
|
seq_printf(m, "bandwidth: %d Hz.\n", bandwidth);
|
|
}
|
|
|
|
if (tuner->used->fe.ops.tuner_ops.get_if_frequency) {
|
|
tuner->used->fe.ops.tuner_ops.get_if_frequency(
|
|
&tuner->used->fe, if_frequency);
|
|
seq_printf(m, "if frequency: %d Hz (IF spectrum %s).\n",
|
|
if_frequency[1],
|
|
if_frequency[0] ? "inverted" : "normal");
|
|
}
|
|
}
|
|
|
|
seq_puts(m, "------------------------------------------------------------\n\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tuner_debug_seq_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, tuner_debug_seq_show, PDE_DATA(inode));
|
|
}
|
|
|
|
static const struct proc_ops tuner_debug_fops = {
|
|
.proc_open = tuner_debug_seq_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release
|
|
};
|
|
|
|
static int demod_debug_seq_show(struct seq_file *m, void *v)
|
|
{
|
|
char *name = NULL;
|
|
struct demod_ops *ops = NULL;
|
|
struct dvb_demod *demod = get_dvb_demods();
|
|
struct dvb_extern_device *dev = m->private;
|
|
|
|
seq_puts(m, "\nDemod Status:\n");
|
|
seq_printf(m, "demod numbers: %d.\n", dev->demod_num);
|
|
seq_puts(m, "all demods:\n");
|
|
list_for_each_entry(ops, &demod->list, list) {
|
|
name = ops->fe ? ops->fe->ops.info.name : "";
|
|
seq_printf(m, "demod%d, id %d (%s) %s, %sregistered, %sdetected, use tuner0 %s, tuner1 %s.\n",
|
|
ops->index, ops->cfg.id,
|
|
name ? name : "", ops->attached ?
|
|
"attached" : "detached",
|
|
ops->registered ? "" : "un",
|
|
ops->cfg.detect ? (ops->valid ? "" : "un") : "no ",
|
|
ops->cfg.tuner0.name ? ops->cfg.tuner0.name : "NONE",
|
|
ops->cfg.tuner1.name ? ops->cfg.tuner1.name : "NONE");
|
|
}
|
|
|
|
if (demod->used && demod->used->fe) {
|
|
name = demod->used->fe->ops.info.name;
|
|
seq_printf(m, "current use demod%d, id %d (%s), fe type %d, delivery system %d.\n",
|
|
demod->used->index,
|
|
demod->used->cfg.id,
|
|
name ? name : "",
|
|
demod->used->type,
|
|
demod->used->fe->dtv_property_cache.delivery_system);
|
|
}
|
|
|
|
seq_puts(m, "\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int demod_debug_seq_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, demod_debug_seq_show, PDE_DATA(inode));
|
|
}
|
|
|
|
static const struct proc_ops demod_debug_fops = {
|
|
.proc_open = demod_debug_seq_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release
|
|
};
|
|
|
|
static int cfgdev_open(struct inode *inode, struct file *filp)
|
|
{
|
|
int num = -1;
|
|
|
|
num = MINOR(inode->i_rdev);
|
|
if (num >= DVB_CFGDEV_COUNT)
|
|
return -ENODEV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dvb_cfgdev_set_tuner(void *pcfg)
|
|
{
|
|
struct tuner_ops *tops = NULL;
|
|
int ret = -1;
|
|
|
|
if (!pcfg)
|
|
return ret;
|
|
|
|
tops = dvb_tuner_ops_create();
|
|
if (!tops) {
|
|
pr_err("create dvb tuner ops fail.\n");
|
|
return ret;
|
|
}
|
|
|
|
memcpy(&tops->cfg, (struct tuner_config *)pcfg, sizeof(struct tuner_config));
|
|
|
|
/*get i2c_adapter*/
|
|
tops->cfg.i2c_adap = i2c_get_adapter(tops->cfg.i2c_id);
|
|
tops->dts_cfg = false;
|
|
tops->index = dvb_tuner_ops_get_index();
|
|
|
|
pr_err("add tuner%d, id %d, i2c_addr 0x%x.\n",
|
|
tops->index, tops->cfg.id, tops->cfg.i2c_addr);
|
|
|
|
/* add to tuner */
|
|
ret = dvb_tuner_ops_add(tops);
|
|
if (ret)
|
|
pr_err("add tuner ops fail, ret = %d.\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long cfgdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct tuner_config cfg_arg;
|
|
struct compact_tuner_config cpt_arg;
|
|
|
|
/*check cmd's validity*/
|
|
if (_IOC_TYPE(cmd) != DVB_CFGDEV_IOC_MAGIC) {
|
|
pr_err("check ioc_type error!");
|
|
return -EINVAL;
|
|
}
|
|
if (_IOC_NR(cmd) > DVB_CFGDEV_IOC_MAXNR) {
|
|
pr_err("check ioc_nr error!");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (cmd == DVB_CFGDEV_IOC_SET_PARAM) {
|
|
pr_err("set param !!");
|
|
if (copy_from_user(&cfg_arg, (struct tuner_config *)arg,
|
|
sizeof(struct tuner_config))) {
|
|
return -EINVAL;
|
|
}
|
|
dvb_cfgdev_set_tuner(&cfg_arg);
|
|
} else if (cmd == COMPAT_DVB_CFGDEV_IOC_SET_PARAM) {
|
|
pr_err("compat set param !!");
|
|
if (copy_from_user(&cpt_arg, (struct compact_tuner_config *)arg,
|
|
sizeof(struct compact_tuner_config))) {
|
|
return -EINVAL;
|
|
}
|
|
cfg_arg.name = NULL;
|
|
cfg_arg.code = cpt_arg.code;
|
|
cfg_arg.id = cpt_arg.id;
|
|
cfg_arg.i2c_addr = cpt_arg.i2c_addr;
|
|
cfg_arg.i2c_id = cpt_arg.i2c_id;
|
|
cfg_arg.i2c_adap = NULL;
|
|
cfg_arg.xtal = cpt_arg.xtal;
|
|
cfg_arg.xtal_cap = cpt_arg.xtal_cap;
|
|
cfg_arg.xtal_mode = cpt_arg.xtal_mode;
|
|
cfg_arg.lt_out = cpt_arg.lt_out;
|
|
cfg_arg.dual_power = cpt_arg.dual_power;
|
|
cfg_arg.if_agc = cpt_arg.if_agc;
|
|
cfg_arg.if_hz = cpt_arg.if_hz;
|
|
cfg_arg.if_invert = cpt_arg.if_invert;
|
|
cfg_arg.if_amp = cpt_arg.if_amp;
|
|
cfg_arg.detect = cpt_arg.detect;
|
|
cfg_arg.reset = cpt_arg.reset;
|
|
cfg_arg.power = cpt_arg.power;
|
|
cfg_arg.reserved0 = cpt_arg.reserved0;
|
|
cfg_arg.reserved1 = cpt_arg.reserved1;
|
|
dvb_cfgdev_set_tuner(&cfg_arg);
|
|
} else if (cmd == DVB_CFGDEV_IOC_GET_PARAM) {
|
|
pr_err("get param !!");
|
|
} else {
|
|
pr_err("default.\n");
|
|
return -EINVAL;
|
|
}
|
|
pr_err("%s --\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long cfgdev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
|
|
cfgdev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static const struct file_operations cfg_dev_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = cfgdev_open,
|
|
.unlocked_ioctl = cfgdev_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = cfgdev_compat_ioctl,
|
|
#endif
|
|
};
|
|
|
|
static CLASS_ATTR_RW(tuner_debug);
|
|
static CLASS_ATTR_RW(demod_debug);
|
|
static CLASS_ATTR_RW(dvb_debug);
|
|
|
|
static struct attribute *dvb_extern_class_attrs[] = {
|
|
&class_attr_tuner_debug.attr,
|
|
&class_attr_demod_debug.attr,
|
|
&class_attr_dvb_debug.attr,
|
|
NULL
|
|
};
|
|
|
|
ATTRIBUTE_GROUPS(dvb_extern_class);
|
|
|
|
static const struct of_device_id aml_dvb_extern_dt_match[] = {
|
|
{
|
|
.compatible = "amlogic, dvb-extern"
|
|
},
|
|
{
|
|
}
|
|
};
|
|
|
|
struct device *aml_get_dvb_extern_dev(void)
|
|
{
|
|
return dvb_extern_dev->dev;
|
|
}
|
|
|
|
static int aml_dvb_extern_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = -1, i = 0;
|
|
unsigned int val = 0;
|
|
char buf[32] = { 0 };
|
|
const char *str = NULL;
|
|
struct tuner_ops *tops = NULL;
|
|
struct demod_ops *dops = NULL;
|
|
struct dvb_extern_device *dvbdev = NULL;
|
|
struct device *pcfgdev = NULL;
|
|
static int cdevno_maj;
|
|
static int cdevno_min;
|
|
|
|
if (!IS_ERR_OR_NULL(dvb_extern_dev))
|
|
goto PROPERTY_DONE;
|
|
|
|
dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL);
|
|
if (!dvbdev) {
|
|
/* pr_err("allocate for dvb extern device fail.\n"); */
|
|
return -ENOMEM;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, dvbdev);
|
|
|
|
dvbdev->name = AML_DVB_EXTERN_DEVICE_NAME;
|
|
dvbdev->dev = &pdev->dev;
|
|
dvbdev->class.name = AML_DVB_EXTERN_DEVICE_NAME;
|
|
dvbdev->class.owner = THIS_MODULE;
|
|
dvbdev->class.class_groups = dvb_extern_class_groups;
|
|
|
|
dvbdev->debug_proc_dir = proc_mkdir(AML_DVB_EXTERN_DEVICE_NAME, NULL);
|
|
if (!dvbdev->debug_proc_dir)
|
|
goto fail_proc_dir;
|
|
|
|
if (!proc_create_data("tuner_debug", 0644, dvbdev->debug_proc_dir,
|
|
&tuner_debug_fops, dvbdev)) {
|
|
pr_err("proc create tuner_debug fail.\n");
|
|
goto fail_proc_create;
|
|
}
|
|
|
|
if (!proc_create_data("demod_debug", 0644, dvbdev->debug_proc_dir,
|
|
&demod_debug_fops, dvbdev)) {
|
|
pr_err("proc create tuner_debug fail.\n");
|
|
goto fail_proc_create;
|
|
}
|
|
|
|
if (class_register(&dvbdev->class)) {
|
|
pr_err("class register fail.\n");
|
|
goto fail_class_register;
|
|
}
|
|
|
|
cdevno_maj = register_chrdev(cdevno_maj, AML_DVB_EXTERN_DEVICE_NAME, &cfg_dev_fops);
|
|
dvbdev->cfgdevno = MKDEV(cdevno_maj, cdevno_min);
|
|
pr_debug("aml_dvb_cfgdev: MAJOR Number %d, MINOR Numbe %d\n",
|
|
MAJOR(dvbdev->cfgdevno), MINOR(dvbdev->cfgdevno));
|
|
|
|
pcfgdev = device_create(&dvbdev->class, NULL, dvbdev->cfgdevno,
|
|
NULL, AML_DVB_EXTERN_DEVICE_NAME);
|
|
if (!pcfgdev) {
|
|
pr_err("%s device create fail.\n", AML_DVB_EXTERN_DEVICE_NAME);
|
|
unregister_chrdev(MAJOR(dvbdev->cfgdevno), AML_DVB_EXTERN_DEVICE_NAME);
|
|
} else {
|
|
dvbdev->cdev = *pcfgdev;
|
|
}
|
|
|
|
/* DVB Power pin. */
|
|
str = NULL;
|
|
ret = of_property_read_string(pdev->dev.of_node,
|
|
"dvb_power_gpio", &str);
|
|
if (ret) {
|
|
dvbdev->dvb_power.pin = -1;
|
|
} else {
|
|
dvbdev->dvb_power.pin = of_get_named_gpio_flags(pdev->dev.of_node,
|
|
"dvb_power_gpio", 0, NULL);
|
|
//pr_err("get dvb_power_gpio: %d.\n", dvbdev->dvb_power.pin);
|
|
}
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node, "dvb_power_dir", &val);
|
|
if (ret) {
|
|
dvbdev->dvb_power.dir = 0;
|
|
} else {
|
|
dvbdev->dvb_power.dir = val;
|
|
//pr_err("get dvb_power_dir: %d (%s).\n",
|
|
// dvbdev->dvb_power.pin, val ? "IN" : "OUT");
|
|
}
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node, "dvb_power_value", &val);
|
|
if (ret) {
|
|
dvbdev->dvb_power.value = 0;
|
|
} else {
|
|
dvbdev->dvb_power.value = val;
|
|
//pr_err("get dvb_power_value: %d.\n", dvbdev->dvb_power.value);
|
|
}
|
|
|
|
/* PROPERTY_TUNER */
|
|
ret = of_property_read_u32(pdev->dev.of_node, "tuner_num", &val);
|
|
if (ret == 0) {
|
|
dvbdev->tuner_num = val;
|
|
pr_debug("find dvb tuner numbers %d.\n", dvbdev->tuner_num);
|
|
} else {
|
|
pr_err("can't find tuner_num.\n");
|
|
goto PROPERTY_DEMOD;
|
|
}
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node, "tuner_cur", &val);
|
|
if (ret)
|
|
dvbdev->tuner_cur = 0;
|
|
else
|
|
dvbdev->tuner_cur = val;
|
|
|
|
for (i = 0; i < dvbdev->tuner_num; ++i) {
|
|
tops = dvb_tuner_ops_create();
|
|
if (!tops) {
|
|
pr_err("create dvb tuner ops fail.\n");
|
|
goto fail_tuner_create;
|
|
}
|
|
|
|
ret = aml_get_dts_tuner_config(pdev->dev.of_node,
|
|
&tops->cfg, i);
|
|
if (ret) {
|
|
dvb_tuner_ops_destroy(tops);
|
|
|
|
pr_err("can't find tuner%d.\n", i);
|
|
} else {
|
|
tops->index = i;
|
|
|
|
pr_debug("find tuner%d, id %d, i2c_addr 0x%x.\n",
|
|
i, tops->cfg.id, tops->cfg.i2c_addr);
|
|
|
|
ret = dvb_tuner_ops_add(tops);
|
|
if (ret)
|
|
pr_err("add tuner ops fail, ret = %d.\n", ret);
|
|
}
|
|
}
|
|
|
|
PROPERTY_DEMOD:
|
|
ret = of_property_read_u32(pdev->dev.of_node, "fe_num", &val);
|
|
if (ret == 0) {
|
|
dvbdev->demod_num = val;
|
|
pr_debug("find dvb fe numbers %d.\n", dvbdev->demod_num);
|
|
} else {
|
|
pr_err("can't find fe_num.\n");
|
|
goto PROPERTY_DONE;
|
|
}
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node, "fe_cur", &val);
|
|
if (ret)
|
|
dvbdev->demod_cur = 0;
|
|
else
|
|
dvbdev->demod_cur = val;
|
|
|
|
for (i = 0; i < dvbdev->demod_num; ++i) {
|
|
dops = dvb_demod_ops_create();
|
|
if (!dops) {
|
|
pr_err("create dvb demod ops fail.\n");
|
|
goto fail_demod_create;
|
|
}
|
|
|
|
ret = aml_get_dts_demod_config(pdev->dev.of_node,
|
|
&dops->cfg, i);
|
|
if (ret) {
|
|
dvb_demod_ops_destroy(dops);
|
|
|
|
pr_err("can't find demod%d.\n", i);
|
|
|
|
continue;
|
|
}
|
|
|
|
dops->index = i;
|
|
|
|
pr_debug("find demod%d, id %d, i2c_addr 0x%x.\n",
|
|
i, dops->cfg.id, dops->cfg.i2c_addr);
|
|
|
|
ret = dvb_demod_ops_add(dops);
|
|
if (ret) {
|
|
pr_err("add demod ops fail, ret = %d.\n", ret);
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Tuner configuration for the external demod.
|
|
* demod includes the tuner driver.
|
|
*/
|
|
/* T/C tuner */
|
|
memset(buf, 0, sizeof(buf));
|
|
snprintf(buf, sizeof(buf), "fe%d_tuner0", i);
|
|
ret = of_property_read_u32(pdev->dev.of_node, buf, &val);
|
|
if (!ret) {
|
|
tops = dvb_tuner_ops_get_byindex(val);
|
|
if (tops)
|
|
memcpy(&dops->cfg.tuner0, &tops->cfg,
|
|
sizeof(struct tuner_config));
|
|
|
|
pr_debug("get %s %d.\n", buf, val);
|
|
}
|
|
|
|
/* S/S2 tuner */
|
|
memset(buf, 0, sizeof(buf));
|
|
snprintf(buf, sizeof(buf), "fe%d_tuner1", i);
|
|
ret = of_property_read_u32(pdev->dev.of_node, buf, &val);
|
|
if (!ret) {
|
|
tops = dvb_tuner_ops_get_byindex(val);
|
|
if (tops)
|
|
memcpy(&dops->cfg.tuner1, &tops->cfg,
|
|
sizeof(struct tuner_config));
|
|
|
|
pr_debug("get %s %d.\n", buf, val);
|
|
}
|
|
}
|
|
|
|
aml_demod_gpio_config(&dvbdev->dvb_power, "dvb_power");
|
|
aml_dvb_extern_set_power(&dvbdev->dvb_power, 1);
|
|
|
|
mutex_init(&dvb_extern_mutex);
|
|
INIT_WORK(&dvbdev->work, aml_dvb_extern_resume_work);
|
|
INIT_WORK(&dvbdev->attach_work.work, aml_dvb_extern_attach_work);
|
|
|
|
PROPERTY_DONE:
|
|
dvb_extern_dev = dvbdev;
|
|
|
|
pr_debug("%s: OK.\n", __func__);
|
|
|
|
return 0;
|
|
|
|
fail_tuner_create:
|
|
fail_demod_create:
|
|
class_unregister(&dvbdev->class);
|
|
|
|
fail_class_register:
|
|
fail_proc_create:
|
|
proc_remove(dvbdev->debug_proc_dir);
|
|
|
|
fail_proc_dir:
|
|
kfree(dvbdev);
|
|
dvbdev = NULL;
|
|
|
|
pr_info("%s: fail, ret = %d.\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aml_dvb_extern_remove(struct platform_device *pdev)
|
|
{
|
|
struct dvb_extern_device *dvbdev = platform_get_drvdata(pdev);
|
|
|
|
if (IS_ERR_OR_NULL(dvbdev))
|
|
return -EFAULT;
|
|
|
|
dvb_tuner_ops_destroy_all();
|
|
dvb_demod_ops_destroy_all();
|
|
|
|
unregister_chrdev(MAJOR(dvbdev->cfgdevno), AML_DVB_EXTERN_DEVICE_NAME);
|
|
device_destroy(&dvbdev->class, dvbdev->cfgdevno);
|
|
|
|
class_unregister(&dvbdev->class);
|
|
if (dvbdev->debug_proc_dir)
|
|
proc_remove(dvbdev->debug_proc_dir);
|
|
|
|
aml_dvb_extern_set_power(&dvbdev->dvb_power, 0);
|
|
|
|
kfree(dvbdev);
|
|
dvbdev = NULL;
|
|
dvb_extern_dev = NULL;
|
|
|
|
pr_info("%s: OK.\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void aml_dvb_extern_shutdown(struct platform_device *pdev)
|
|
{
|
|
struct dvb_extern_device *dvbdev = platform_get_drvdata(pdev);
|
|
struct tuner_ops *tops = NULL;
|
|
struct dvb_tuner *tuner = get_dvb_tuners();
|
|
|
|
if (IS_ERR_OR_NULL(dvbdev))
|
|
return;
|
|
|
|
list_for_each_entry(tops, &tuner->list, list) {
|
|
if (tops->fe.ops.tuner_ops.suspend)
|
|
tops->fe.ops.tuner_ops.suspend(&tops->fe);
|
|
else if (tops->fe.ops.tuner_ops.sleep)
|
|
tops->fe.ops.tuner_ops.sleep(&tops->fe);
|
|
}
|
|
|
|
aml_dvb_extern_set_power(&dvbdev->dvb_power, 0);
|
|
}
|
|
|
|
static int aml_dvb_extern_suspend(struct platform_device *pdev,
|
|
pm_message_t state)
|
|
{
|
|
struct dvb_extern_device *dvbdev = platform_get_drvdata(pdev);
|
|
struct tuner_ops *tops = NULL;
|
|
struct dvb_tuner *tuner = get_dvb_tuners();
|
|
|
|
if (IS_ERR_OR_NULL(dvbdev))
|
|
return -EFAULT;
|
|
|
|
list_for_each_entry(tops, &tuner->list, list) {
|
|
if (tops->fe.ops.tuner_ops.suspend)
|
|
tops->fe.ops.tuner_ops.suspend(&tops->fe);
|
|
else if (tops->fe.ops.tuner_ops.sleep)
|
|
tops->fe.ops.tuner_ops.sleep(&tops->fe);
|
|
}
|
|
|
|
aml_dvb_extern_set_power(&dvbdev->dvb_power, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aml_dvb_extern_resume(struct platform_device *pdev)
|
|
{
|
|
struct dvb_extern_device *dvbdev = platform_get_drvdata(pdev);
|
|
|
|
if (IS_ERR_OR_NULL(dvbdev))
|
|
return -EFAULT;
|
|
|
|
aml_dvb_extern_set_power(&dvbdev->dvb_power, 1);
|
|
|
|
schedule_work(&dvbdev->work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver aml_dvb_extern_driver = {
|
|
.driver = {
|
|
.name = AML_DVB_EXTERN_DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = aml_dvb_extern_dt_match
|
|
},
|
|
.probe = aml_dvb_extern_probe,
|
|
.remove = aml_dvb_extern_remove,
|
|
.shutdown = aml_dvb_extern_shutdown,
|
|
.suspend = aml_dvb_extern_suspend,
|
|
.resume = aml_dvb_extern_resume
|
|
};
|
|
|
|
int __init aml_dvb_extern_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = platform_driver_register(&aml_dvb_extern_driver);
|
|
if (ret) {
|
|
pr_err("%s: failed to register driver, ret %d.\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
pr_info("%s: OK, version: %s.\n", __func__, AML_DVB_EXTERN_VERSION);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void __exit aml_dvb_extern_exit(void)
|
|
{
|
|
platform_driver_unregister(&aml_dvb_extern_driver);
|
|
|
|
pr_info("%s: OK.\n", __func__);
|
|
}
|