summaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb-frontends/si2165.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-02-06 20:27:48 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2018-02-06 20:27:48 +0100
commit68c5735eaa5e680e701c9a2d1e3c7880bdf5ab66 (patch)
tree4f584693638bf257b66a1646cc30d823cacc0a58 /drivers/media/dvb-frontends/si2165.c
parentMerge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma (diff)
parentmedia: v4l2-compat-ioctl32.c: make ctrl_is_pointer work for subdevs (diff)
downloadlinux-68c5735eaa5e680e701c9a2d1e3c7880bdf5ab66.tar.xz
linux-68c5735eaa5e680e701c9a2d1e3c7880bdf5ab66.zip
Merge tag 'media/v4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - videobuf2 was moved to a media/common dir, as it is now used by the DVB subsystem too - Digital TV core memory mapped support interface - new sensor driver: ov7740 - several improvements at ddbridge driver - new V4L2 driver: IPU3 CIO2 CSI-2 receiver unit, found on some Intel SoCs - new tuner driver: tda18250 - finally got rid of all LIRC staging drivers - as we don't have old lirc drivers anymore, restruct the lirc device code - add support for UVC metadata - add a new staging driver for NVIDIA Tegra Video Decoder Engine - DVB kAPI headers moved to include/media - synchronize the kAPI and uAPI for the DVB subsystem, removing the gap for non-legacy APIs - reduce the kAPI gap for V4L2 - lots of other driver enhancements, cleanups, etc. * tag 'media/v4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (407 commits) media: v4l2-compat-ioctl32.c: make ctrl_is_pointer work for subdevs media: v4l2-compat-ioctl32.c: refactor compat ioctl32 logic media: v4l2-compat-ioctl32.c: don't copy back the result for certain errors media: v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type media: v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32 media: v4l2-compat-ioctl32.c: fix ctrl_is_pointer media: v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32 media: v4l2-compat-ioctl32.c: avoid sizeof(type) media: v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32 media: v4l2-compat-ioctl32.c: fix the indentation media: v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF media: v4l2-ioctl.c: don't copy back the result for -ENOTTY media: v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt media: vivid: fix module load error when enabling fb and no_error_inj=1 media: dvb_demux: improve debug messages media: dvb_demux: Better handle discontinuity errors media: cxusb, dib0700: ignore XC2028_I2C_FLUSH media: ts2020: avoid integer overflows on 32 bit machines media: i2c: ov7740: use gpio/consumer.h instead of gpio.h media: entity: Add a nop variant of media_entity_cleanup ...
Diffstat (limited to 'drivers/media/dvb-frontends/si2165.c')
-rw-r--r--drivers/media/dvb-frontends/si2165.c613
1 files changed, 373 insertions, 240 deletions
diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
index 528b82a5dd46..2dd336f95cbf 100644
--- a/drivers/media/dvb-frontends/si2165.c
+++ b/drivers/media/dvb-frontends/si2165.c
@@ -1,7 +1,7 @@
/*
* Driver for Silicon Labs Si2161 DVB-T and Si2165 DVB-C/-T Demodulator
*
- * Copyright (C) 2013-2014 Matthias Schwarzott <zzam@gentoo.org>
+ * Copyright (C) 2013-2017 Matthias Schwarzott <zzam@gentoo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,8 +27,8 @@
#include <linux/firmware.h>
#include <linux/regmap.h>
-#include "dvb_frontend.h"
-#include "dvb_math.h"
+#include <media/dvb_frontend.h>
+#include <media/dvb_math.h>
#include "si2165_priv.h"
#include "si2165.h"
@@ -57,63 +57,21 @@ struct si2165_state {
u32 sys_clk;
u32 adc_clk;
+ /* DVBv3 stats */
+ u64 ber_prev;
+
bool has_dvbc;
bool has_dvbt;
bool firmware_loaded;
};
-#define DEBUG_OTHER 0x01
-#define DEBUG_I2C_WRITE 0x02
-#define DEBUG_I2C_READ 0x04
-#define DEBUG_REG_READ 0x08
-#define DEBUG_REG_WRITE 0x10
-#define DEBUG_FW_LOAD 0x20
-
-static int debug = 0x00;
-
-#define dprintk(args...) \
- do { \
- if (debug & DEBUG_OTHER) \
- printk(KERN_DEBUG "si2165: " args); \
- } while (0)
-
-#define deb_i2c_write(args...) \
- do { \
- if (debug & DEBUG_I2C_WRITE) \
- printk(KERN_DEBUG "si2165: i2c write: " args); \
- } while (0)
-
-#define deb_i2c_read(args...) \
- do { \
- if (debug & DEBUG_I2C_READ) \
- printk(KERN_DEBUG "si2165: i2c read: " args); \
- } while (0)
-
-#define deb_readreg(args...) \
- do { \
- if (debug & DEBUG_REG_READ) \
- printk(KERN_DEBUG "si2165: reg read: " args); \
- } while (0)
-
-#define deb_writereg(args...) \
- do { \
- if (debug & DEBUG_REG_WRITE) \
- printk(KERN_DEBUG "si2165: reg write: " args); \
- } while (0)
-
-#define deb_fw_load(args...) \
- do { \
- if (debug & DEBUG_FW_LOAD) \
- printk(KERN_DEBUG "si2165: fw load: " args); \
- } while (0)
-
static int si2165_write(struct si2165_state *state, const u16 reg,
- const u8 *src, const int count)
+ const u8 *src, const int count)
{
int ret;
- if (debug & DEBUG_I2C_WRITE)
- deb_i2c_write("reg: 0x%04x, data: %*ph\n", reg, count, src);
+ dev_dbg(&state->client->dev, "i2c write: reg: 0x%04x, data: %*ph\n",
+ reg, count, src);
ret = regmap_bulk_write(state->regmap, reg, src, count);
@@ -134,30 +92,41 @@ static int si2165_read(struct si2165_state *state,
return ret;
}
- if (debug & DEBUG_I2C_READ)
- deb_i2c_read("reg: 0x%04x, data: %*ph\n", reg, count, val);
+ dev_dbg(&state->client->dev, "i2c read: reg: 0x%04x, data: %*ph\n",
+ reg, count, val);
return 0;
}
static int si2165_readreg8(struct si2165_state *state,
- const u16 reg, u8 *val)
+ const u16 reg, u8 *val)
{
unsigned int val_tmp;
int ret = regmap_read(state->regmap, reg, &val_tmp);
*val = (u8)val_tmp;
- deb_readreg("R(0x%04x)=0x%02x\n", reg, *val);
+ dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%02x\n", reg, *val);
return ret;
}
static int si2165_readreg16(struct si2165_state *state,
- const u16 reg, u16 *val)
+ const u16 reg, u16 *val)
{
u8 buf[2];
int ret = si2165_read(state, reg, buf, 2);
*val = buf[0] | buf[1] << 8;
- deb_readreg("R(0x%04x)=0x%04x\n", reg, *val);
+ dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%04x\n", reg, *val);
+ return ret;
+}
+
+static int si2165_readreg24(struct si2165_state *state,
+ const u16 reg, u32 *val)
+{
+ u8 buf[3];
+
+ int ret = si2165_read(state, reg, buf, 3);
+ *val = buf[0] | buf[1] << 8 | buf[2] << 16;
+ dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%06x\n", reg, *val);
return ret;
}
@@ -208,7 +177,9 @@ static int si2165_writereg_mask8(struct si2165_state *state, const u16 reg,
return si2165_writereg8(state, reg, val);
}
-#define REG16(reg, val) { (reg), (val) & 0xff }, { (reg)+1, (val)>>8 & 0xff }
+#define REG16(reg, val) \
+ { (reg), (val) & 0xff }, \
+ { (reg) + 1, (val) >> 8 & 0xff }
struct si2165_reg_value_pair {
u16 reg;
u8 val;
@@ -238,7 +209,7 @@ static int si2165_get_tune_settings(struct dvb_frontend *fe,
static int si2165_init_pll(struct si2165_state *state)
{
- u32 ref_freq_Hz = state->config.ref_freq_Hz;
+ u32 ref_freq_hz = state->config.ref_freq_hz;
u8 divr = 1; /* 1..7 */
u8 divp = 1; /* only 1 or 4 */
u8 divn = 56; /* 1..63 */
@@ -250,7 +221,7 @@ static int si2165_init_pll(struct si2165_state *state)
* hardcoded values can be deleted if calculation is verified
* or it yields the same values as the windows driver
*/
- switch (ref_freq_Hz) {
+ switch (ref_freq_hz) {
case 16000000u:
divn = 56;
break;
@@ -261,39 +232,39 @@ static int si2165_init_pll(struct si2165_state *state)
break;
default:
/* ref_freq / divr must be between 4 and 16 MHz */
- if (ref_freq_Hz > 16000000u)
+ if (ref_freq_hz > 16000000u)
divr = 2;
/*
* now select divn and divp such that
* fvco is in 1624..1824 MHz
*/
- if (1624000000u * divr > ref_freq_Hz * 2u * 63u)
+ if (1624000000u * divr > ref_freq_hz * 2u * 63u)
divp = 4;
/* is this already correct regarding rounding? */
- divn = 1624000000u * divr / (ref_freq_Hz * 2u * divp);
+ divn = 1624000000u * divr / (ref_freq_hz * 2u * divp);
break;
}
/* adc_clk and sys_clk depend on xtal and pll settings */
- state->fvco_hz = ref_freq_Hz / divr
+ state->fvco_hz = ref_freq_hz / divr
* 2u * divn * divp;
state->adc_clk = state->fvco_hz / (divm * 4u);
state->sys_clk = state->fvco_hz / (divl * 2u);
- /* write pll registers 0x00a0..0x00a3 at once */
+ /* write all 4 pll registers 0x00a0..0x00a3 at once */
buf[0] = divl;
buf[1] = divm;
buf[2] = (divn & 0x3f) | ((divp == 1) ? 0x40 : 0x00) | 0x80;
buf[3] = divr;
- return si2165_write(state, 0x00a0, buf, 4);
+ return si2165_write(state, REG_PLL_DIVL, buf, 4);
}
static int si2165_adjust_pll_divl(struct si2165_state *state, u8 divl)
{
state->sys_clk = state->fvco_hz / (divl * 2u);
- return si2165_writereg8(state, 0x00a0, divl); /* pll_divl */
+ return si2165_writereg8(state, REG_PLL_DIVL, divl);
}
static u32 si2165_get_fe_clk(struct si2165_state *state)
@@ -309,18 +280,18 @@ static int si2165_wait_init_done(struct si2165_state *state)
int i;
for (i = 0; i < 3; ++i) {
- si2165_readreg8(state, 0x0054, &val);
+ si2165_readreg8(state, REG_INIT_DONE, &val);
if (val == 0x01)
return 0;
usleep_range(1000, 50000);
}
- dev_err(&state->client->dev, "%s: init_done was not set\n",
- KBUILD_MODNAME);
+ dev_err(&state->client->dev, "init_done was not set\n");
return ret;
}
static int si2165_upload_firmware_block(struct si2165_state *state,
- const u8 *data, u32 len, u32 *poffset, u32 block_count)
+ const u8 *data, u32 len, u32 *poffset,
+ u32 block_count)
{
int ret;
u8 buf_ctrl[4] = { 0x00, 0x00, 0x00, 0xc0 };
@@ -333,42 +304,43 @@ static int si2165_upload_firmware_block(struct si2165_state *state,
if (len % 4 != 0)
return -EINVAL;
- deb_fw_load(
- "si2165_upload_firmware_block called with len=0x%x offset=0x%x blockcount=0x%x\n",
- len, offset, block_count);
- while (offset+12 <= len && cur_block < block_count) {
- deb_fw_load(
- "si2165_upload_firmware_block in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
- len, offset, cur_block, block_count);
+ dev_dbg(&state->client->dev,
+ "fw load: %s: called with len=0x%x offset=0x%x blockcount=0x%x\n",
+ __func__, len, offset, block_count);
+ while (offset + 12 <= len && cur_block < block_count) {
+ dev_dbg(&state->client->dev,
+ "fw load: %s: in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
+ __func__, len, offset, cur_block, block_count);
wordcount = data[offset];
- if (wordcount < 1 || data[offset+1] ||
- data[offset+2] || data[offset+3]) {
+ if (wordcount < 1 || data[offset + 1] ||
+ data[offset + 2] || data[offset + 3]) {
dev_warn(&state->client->dev,
- "%s: bad fw data[0..3] = %*ph\n",
- KBUILD_MODNAME, 4, data);
+ "bad fw data[0..3] = %*ph\n",
+ 4, data);
return -EINVAL;
}
if (offset + 8 + wordcount * 4 > len) {
dev_warn(&state->client->dev,
- "%s: len is too small for block len=%d, wordcount=%d\n",
- KBUILD_MODNAME, len, wordcount);
+ "len is too small for block len=%d, wordcount=%d\n",
+ len, wordcount);
return -EINVAL;
}
buf_ctrl[0] = wordcount - 1;
- ret = si2165_write(state, 0x0364, buf_ctrl, 4);
+ ret = si2165_write(state, REG_DCOM_CONTROL_BYTE, buf_ctrl, 4);
if (ret < 0)
goto error;
- ret = si2165_write(state, 0x0368, data+offset+4, 4);
+ ret = si2165_write(state, REG_DCOM_ADDR, data + offset + 4, 4);
if (ret < 0)
goto error;
offset += 8;
while (wordcount > 0) {
- ret = si2165_write(state, 0x36c, data+offset, 4);
+ ret = si2165_write(state, REG_DCOM_DATA,
+ data + offset, 4);
if (ret < 0)
goto error;
wordcount--;
@@ -377,15 +349,16 @@ static int si2165_upload_firmware_block(struct si2165_state *state,
cur_block++;
}
- deb_fw_load(
- "si2165_upload_firmware_block after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
- len, offset, cur_block, block_count);
+ dev_dbg(&state->client->dev,
+ "fw load: %s: after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
+ __func__, len, offset, cur_block, block_count);
if (poffset)
*poffset = offset;
- deb_fw_load("si2165_upload_firmware_block returned offset=0x%x\n",
- offset);
+ dev_dbg(&state->client->dev,
+ "fw load: %s: returned offset=0x%x\n",
+ __func__, offset);
return 0;
error:
@@ -413,43 +386,40 @@ static int si2165_upload_firmware(struct si2165_state *state)
fw_file = SI2165_FIRMWARE_REV_D;
break;
default:
- dev_info(&state->client->dev, "%s: no firmware file for revision=%d\n",
- KBUILD_MODNAME, state->chip_revcode);
+ dev_info(&state->client->dev, "no firmware file for revision=%d\n",
+ state->chip_revcode);
return 0;
}
/* request the firmware, this will block and timeout */
ret = request_firmware(&fw, fw_file, &state->client->dev);
if (ret) {
- dev_warn(&state->client->dev, "%s: firmware file '%s' not found\n",
- KBUILD_MODNAME, fw_file);
+ dev_warn(&state->client->dev, "firmware file '%s' not found\n",
+ fw_file);
goto error;
}
data = fw->data;
len = fw->size;
- dev_info(&state->client->dev, "%s: downloading firmware from file '%s' size=%d\n",
- KBUILD_MODNAME, fw_file, len);
+ dev_info(&state->client->dev, "downloading firmware from file '%s' size=%d\n",
+ fw_file, len);
if (len % 4 != 0) {
- dev_warn(&state->client->dev, "%s: firmware size is not multiple of 4\n",
- KBUILD_MODNAME);
+ dev_warn(&state->client->dev, "firmware size is not multiple of 4\n");
ret = -EINVAL;
goto error;
}
/* check header (8 bytes) */
if (len < 8) {
- dev_warn(&state->client->dev, "%s: firmware header is missing\n",
- KBUILD_MODNAME);
+ dev_warn(&state->client->dev, "firmware header is missing\n");
ret = -EINVAL;
goto error;
}
if (data[0] != 1 || data[1] != 0) {
- dev_warn(&state->client->dev, "%s: firmware file version is wrong\n",
- KBUILD_MODNAME);
+ dev_warn(&state->client->dev, "firmware file version is wrong\n");
ret = -EINVAL;
goto error;
}
@@ -460,45 +430,45 @@ static int si2165_upload_firmware(struct si2165_state *state)
/* start uploading fw */
/* boot/wdog status */
- ret = si2165_writereg8(state, 0x0341, 0x00);
+ ret = si2165_writereg8(state, REG_WDOG_AND_BOOT, 0x00);
if (ret < 0)
goto error;
/* reset */
- ret = si2165_writereg8(state, 0x00c0, 0x00);
+ ret = si2165_writereg8(state, REG_RST_ALL, 0x00);
if (ret < 0)
goto error;
/* boot/wdog status */
- ret = si2165_readreg8(state, 0x0341, val);
+ ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, val);
if (ret < 0)
goto error;
/* enable reset on error */
- ret = si2165_readreg8(state, 0x035c, val);
+ ret = si2165_readreg8(state, REG_EN_RST_ERROR, val);
if (ret < 0)
goto error;
- ret = si2165_readreg8(state, 0x035c, val);
+ ret = si2165_readreg8(state, REG_EN_RST_ERROR, val);
if (ret < 0)
goto error;
- ret = si2165_writereg8(state, 0x035c, 0x02);
+ ret = si2165_writereg8(state, REG_EN_RST_ERROR, 0x02);
if (ret < 0)
goto error;
/* start right after the header */
offset = 8;
- dev_info(&state->client->dev, "%s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n",
- KBUILD_MODNAME, patch_version, block_count, crc_expected);
+ dev_info(&state->client->dev, "%s: extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n",
+ __func__, patch_version, block_count, crc_expected);
ret = si2165_upload_firmware_block(state, data, len, &offset, 1);
if (ret < 0)
goto error;
- ret = si2165_writereg8(state, 0x0344, patch_version);
+ ret = si2165_writereg8(state, REG_PATCH_VERSION, patch_version);
if (ret < 0)
goto error;
/* reset crc */
- ret = si2165_writereg8(state, 0x0379, 0x01);
+ ret = si2165_writereg8(state, REG_RST_CRC, 0x01);
if (ret)
goto error;
@@ -506,20 +476,19 @@ static int si2165_upload_firmware(struct si2165_state *state)
&offset, block_count);
if (ret < 0) {
dev_err(&state->client->dev,
- "%s: firmware could not be uploaded\n",
- KBUILD_MODNAME);
+ "firmware could not be uploaded\n");
goto error;
}
/* read crc */
- ret = si2165_readreg16(state, 0x037a, &val16);
+ ret = si2165_readreg16(state, REG_CRC, &val16);
if (ret)
goto error;
if (val16 != crc_expected) {
dev_err(&state->client->dev,
- "%s: firmware crc mismatch %04x != %04x\n",
- KBUILD_MODNAME, val16, crc_expected);
+ "firmware crc mismatch %04x != %04x\n",
+ val16, crc_expected);
ret = -EINVAL;
goto error;
}
@@ -530,23 +499,23 @@ static int si2165_upload_firmware(struct si2165_state *state)
if (len != offset) {
dev_err(&state->client->dev,
- "%s: firmware len mismatch %04x != %04x\n",
- KBUILD_MODNAME, len, offset);
+ "firmware len mismatch %04x != %04x\n",
+ len, offset);
ret = -EINVAL;
goto error;
}
/* reset watchdog error register */
- ret = si2165_writereg_mask8(state, 0x0341, 0x02, 0x02);
+ ret = si2165_writereg_mask8(state, REG_WDOG_AND_BOOT, 0x02, 0x02);
if (ret < 0)
goto error;
/* enable reset on error */
- ret = si2165_writereg_mask8(state, 0x035c, 0x01, 0x01);
+ ret = si2165_writereg_mask8(state, REG_EN_RST_ERROR, 0x01, 0x01);
if (ret < 0)
goto error;
- dev_info(&state->client->dev, "%s: fw load finished\n", KBUILD_MODNAME);
+ dev_info(&state->client->dev, "fw load finished\n");
ret = 0;
state->firmware_loaded = true;
@@ -563,46 +532,47 @@ static int si2165_init(struct dvb_frontend *fe)
{
int ret = 0;
struct si2165_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
u8 val;
u8 patch_version = 0x00;
- dprintk("%s: called\n", __func__);
+ dev_dbg(&state->client->dev, "%s: called\n", __func__);
/* powerup */
- ret = si2165_writereg8(state, 0x0000, state->config.chip_mode);
+ ret = si2165_writereg8(state, REG_CHIP_MODE, state->config.chip_mode);
if (ret < 0)
goto error;
/* dsp_clock_enable */
- ret = si2165_writereg8(state, 0x0104, 0x01);
+ ret = si2165_writereg8(state, REG_DSP_CLOCK, 0x01);
if (ret < 0)
goto error;
- ret = si2165_readreg8(state, 0x0000, &val); /* verify chip_mode */
+ /* verify chip_mode */
+ ret = si2165_readreg8(state, REG_CHIP_MODE, &val);
if (ret < 0)
goto error;
if (val != state->config.chip_mode) {
- dev_err(&state->client->dev, "%s: could not set chip_mode\n",
- KBUILD_MODNAME);
+ dev_err(&state->client->dev, "could not set chip_mode\n");
return -EINVAL;
}
/* agc */
- ret = si2165_writereg8(state, 0x018b, 0x00);
+ ret = si2165_writereg8(state, REG_AGC_IF_TRI, 0x00);
if (ret < 0)
goto error;
- ret = si2165_writereg8(state, 0x0190, 0x01);
+ ret = si2165_writereg8(state, REG_AGC_IF_SLR, 0x01);
if (ret < 0)
goto error;
- ret = si2165_writereg8(state, 0x0170, 0x00);
+ ret = si2165_writereg8(state, REG_AGC2_OUTPUT, 0x00);
if (ret < 0)
goto error;
- ret = si2165_writereg8(state, 0x0171, 0x07);
+ ret = si2165_writereg8(state, REG_AGC2_CLKDIV, 0x07);
if (ret < 0)
goto error;
/* rssi pad */
- ret = si2165_writereg8(state, 0x0646, 0x00);
+ ret = si2165_writereg8(state, REG_RSSI_PAD_CTRL, 0x00);
if (ret < 0)
goto error;
- ret = si2165_writereg8(state, 0x0641, 0x00);
+ ret = si2165_writereg8(state, REG_RSSI_ENABLE, 0x00);
if (ret < 0)
goto error;
@@ -611,11 +581,11 @@ static int si2165_init(struct dvb_frontend *fe)
goto error;
/* enable chip_init */
- ret = si2165_writereg8(state, 0x0050, 0x01);
+ ret = si2165_writereg8(state, REG_CHIP_INIT, 0x01);
if (ret < 0)
goto error;
/* set start_init */
- ret = si2165_writereg8(state, 0x0096, 0x01);
+ ret = si2165_writereg8(state, REG_START_INIT, 0x01);
if (ret < 0)
goto error;
ret = si2165_wait_init_done(state);
@@ -623,29 +593,30 @@ static int si2165_init(struct dvb_frontend *fe)
goto error;
/* disable chip_init */
- ret = si2165_writereg8(state, 0x0050, 0x00);
+ ret = si2165_writereg8(state, REG_CHIP_INIT, 0x00);
if (ret < 0)
goto error;
- /* ber_pkt */
- ret = si2165_writereg16(state, 0x0470, 0x7530);
+ /* ber_pkt - default 65535 */
+ ret = si2165_writereg16(state, REG_BER_PKT,
+ STATISTICS_PERIOD_PKT_COUNT);
if (ret < 0)
goto error;
- ret = si2165_readreg8(state, 0x0344, &patch_version);
+ ret = si2165_readreg8(state, REG_PATCH_VERSION, &patch_version);
if (ret < 0)
goto error;
- ret = si2165_writereg8(state, 0x00cb, 0x00);
+ ret = si2165_writereg8(state, REG_AUTO_RESET, 0x00);
if (ret < 0)
goto error;
/* dsp_addr_jump */
- ret = si2165_writereg32(state, 0x0348, 0xf4000000);
+ ret = si2165_writereg32(state, REG_ADDR_JUMP, 0xf4000000);
if (ret < 0)
goto error;
/* boot/wdog status */
- ret = si2165_readreg8(state, 0x0341, &val);
+ ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, &val);
if (ret < 0)
goto error;
@@ -656,19 +627,30 @@ static int si2165_init(struct dvb_frontend *fe)
}
/* ts output config */
- ret = si2165_writereg8(state, 0x04e4, 0x20);
+ ret = si2165_writereg8(state, REG_TS_DATA_MODE, 0x20);
+ if (ret < 0)
+ return ret;
+ ret = si2165_writereg16(state, REG_TS_TRI, 0x00fe);
if (ret < 0)
return ret;
- ret = si2165_writereg16(state, 0x04ef, 0x00fe);
+ ret = si2165_writereg24(state, REG_TS_SLR, 0x555555);
if (ret < 0)
return ret;
- ret = si2165_writereg24(state, 0x04f4, 0x555555);
+ ret = si2165_writereg8(state, REG_TS_CLK_MODE, 0x01);
if (ret < 0)
return ret;
- ret = si2165_writereg8(state, 0x04e5, 0x01);
+ ret = si2165_writereg8(state, REG_TS_PARALLEL_MODE, 0x00);
if (ret < 0)
return ret;
+ c = &state->fe.dtv_property_cache;
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
return 0;
error:
return ret;
@@ -680,11 +662,11 @@ static int si2165_sleep(struct dvb_frontend *fe)
struct si2165_state *state = fe->demodulator_priv;
/* dsp clock disable */
- ret = si2165_writereg8(state, 0x0104, 0x00);
+ ret = si2165_writereg8(state, REG_DSP_CLOCK, 0x00);
if (ret < 0)
return ret;
/* chip mode */
- ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF);
+ ret = si2165_writereg8(state, REG_CHIP_MODE, SI2165_MODE_OFF);
if (ret < 0)
return ret;
return 0;
@@ -693,18 +675,48 @@ static int si2165_sleep(struct dvb_frontend *fe)
static int si2165_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
int ret;
- u8 fec_lock = 0;
+ u8 u8tmp;
+ u32 u32tmp;
struct si2165_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 delsys = c->delivery_system;
- if (!state->has_dvbt)
- return -EINVAL;
+ *status = 0;
+
+ switch (delsys) {
+ case SYS_DVBT:
+ /* check fast signal type */
+ ret = si2165_readreg8(state, REG_CHECK_SIGNAL, &u8tmp);
+ if (ret < 0)
+ return ret;
+ switch (u8tmp & 0x3) {
+ case 0: /* searching */
+ case 1: /* nothing */
+ break;
+ case 2: /* digital signal */
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ break;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ /* check packet sync lock */
+ ret = si2165_readreg8(state, REG_PS_LOCK, &u8tmp);
+ if (ret < 0)
+ return ret;
+ if (u8tmp & 0x01) {
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ }
+ break;
+ }
/* check fec_lock */
- ret = si2165_readreg8(state, 0x4e0, &fec_lock);
+ ret = si2165_readreg8(state, REG_FEC_LOCK, &u8tmp);
if (ret < 0)
return ret;
- *status = 0;
- if (fec_lock & 0x01) {
+ if (u8tmp & 0x01) {
*status |= FE_HAS_SIGNAL;
*status |= FE_HAS_CARRIER;
*status |= FE_HAS_VITERBI;
@@ -712,6 +724,109 @@ static int si2165_read_status(struct dvb_frontend *fe, enum fe_status *status)
*status |= FE_HAS_LOCK;
}
+ /* CNR */
+ if (delsys == SYS_DVBC_ANNEX_A && *status & FE_HAS_VITERBI) {
+ ret = si2165_readreg24(state, REG_C_N, &u32tmp);
+ if (ret < 0)
+ return ret;
+ /*
+ * svalue =
+ * 1000 * c_n/dB =
+ * 1000 * 10 * log10(2^24 / regval) =
+ * 1000 * 10 * (log10(2^24) - log10(regval)) =
+ * 1000 * 10 * (intlog10(2^24) - intlog10(regval)) / 2^24
+ *
+ * intlog10(x) = log10(x) * 2^24
+ * intlog10(2^24) = log10(2^24) * 2^24 = 121210686
+ */
+ u32tmp = (1000 * 10 * (121210686 - (u64)intlog10(u32tmp)))
+ >> 24;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = u32tmp;
+ } else
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ /* BER */
+ if (*status & FE_HAS_VITERBI) {
+ if (c->post_bit_error.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ /* start new sampling period to get rid of old data*/
+ ret = si2165_writereg8(state, REG_BER_RST, 0x01);
+ if (ret < 0)
+ return ret;
+
+ /* set scale to enter read code on next call */
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = 0;
+ c->post_bit_count.stat[0].uvalue = 0;
+
+ /*
+ * reset DVBv3 value to deliver a good result
+ * for the first call
+ */
+ state->ber_prev = 0;
+
+ } else {
+ ret = si2165_readreg8(state, REG_BER_AVAIL, &u8tmp);
+ if (ret < 0)
+ return ret;
+
+ if (u8tmp & 1) {
+ u32 biterrcnt;
+
+ ret = si2165_readreg24(state, REG_BER_BIT,
+ &biterrcnt);
+ if (ret < 0)
+ return ret;
+
+ c->post_bit_error.stat[0].uvalue +=
+ biterrcnt;
+ c->post_bit_count.stat[0].uvalue +=
+ STATISTICS_PERIOD_BIT_COUNT;
+
+ /* start new sampling period */
+ ret = si2165_writereg8(state,
+ REG_BER_RST, 0x01);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&state->client->dev,
+ "post_bit_error=%u post_bit_count=%u\n",
+ biterrcnt, STATISTICS_PERIOD_BIT_COUNT);
+ }
+ }
+ } else {
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+}
+
+static int si2165_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+ else
+ *snr = 0;
+ return 0;
+}
+
+static int si2165_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct si2165_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->post_bit_error.stat[0].scale != FE_SCALE_COUNTER) {
+ *ber = 0;
+ return 0;
+ }
+
+ *ber = c->post_bit_error.stat[0].uvalue - state->ber_prev;
+ state->ber_prev = c->post_bit_error.stat[0].uvalue;
+
return 0;
}
@@ -728,8 +843,8 @@ static int si2165_set_oversamp(struct si2165_state *state, u32 dvb_rate)
do_div(oversamp, dvb_rate);
reg_value = oversamp & 0x3fffffff;
- dprintk("%s: Write oversamp=%#x\n", __func__, reg_value);
- return si2165_writereg32(state, 0x00e4, reg_value);
+ dev_dbg(&state->client->dev, "Write oversamp=%#x\n", reg_value);
+ return si2165_writereg32(state, REG_OVERSAMP, reg_value);
}
static int si2165_set_if_freq_shift(struct si2165_state *state)
@@ -742,8 +857,7 @@ static int si2165_set_if_freq_shift(struct si2165_state *state)
if (!fe->ops.tuner_ops.get_if_frequency) {
dev_err(&state->client->dev,
- "%s: Error: get_if_frequency() not defined at tuner. Can't work without it!\n",
- KBUILD_MODNAME);
+ "Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
return -EINVAL;
}
@@ -763,30 +877,29 @@ static int si2165_set_if_freq_shift(struct si2165_state *state)
reg_value = reg_value & 0x1fffffff;
/* if_freq_shift, usbdump contained 0x023ee08f; */
- return si2165_writereg32(state, 0x00e8, reg_value);
+ return si2165_writereg32(state, REG_IF_FREQ_SHIFT, reg_value);
}
static const struct si2165_reg_value_pair dvbt_regs[] = {
/* standard = DVB-T */
- { 0x00ec, 0x01 },
- { 0x08f8, 0x00 },
+ { REG_DVB_STANDARD, 0x01 },
/* impulsive_noise_remover */
- { 0x031c, 0x01 },
- { 0x00cb, 0x00 },
+ { REG_IMPULSIVE_NOISE_REM, 0x01 },
+ { REG_AUTO_RESET, 0x00 },
/* agc2 */
- { 0x016e, 0x41 },
- { 0x016c, 0x0e },
- { 0x016d, 0x10 },
+ { REG_AGC2_MIN, 0x41 },
+ { REG_AGC2_KACQ, 0x0e },
+ { REG_AGC2_KLOC, 0x10 },
/* agc */
- { 0x015b, 0x03 },
- { 0x0150, 0x78 },
+ { REG_AGC_UNFREEZE_THR, 0x03 },
+ { REG_AGC_CRESTF_DBX8, 0x78 },
/* agc */
- { 0x01a0, 0x78 },
- { 0x01c8, 0x68 },
+ { REG_AAF_CRESTF_DBX8, 0x78 },
+ { REG_ACI_CRESTF_DBX8, 0x68 },
/* freq_sync_range */
- REG16(0x030c, 0x0064),
+ REG16(REG_FREQ_SYNC_RANGE, 0x0064),
/* gp_reg0 */
- { 0x0387, 0x00 }
+ { REG_GP_REG0_MSB, 0x00 }
};
static int si2165_set_frontend_dvbt(struct dvb_frontend *fe)
@@ -798,7 +911,7 @@ static int si2165_set_frontend_dvbt(struct dvb_frontend *fe)
u16 bw10k;
u32 bw_hz = p->bandwidth_hz;
- dprintk("%s: called\n", __func__);
+ dev_dbg(&state->client->dev, "%s: called\n", __func__);
if (!state->has_dvbt)
return -EINVAL;
@@ -815,7 +928,7 @@ static int si2165_set_frontend_dvbt(struct dvb_frontend *fe)
return ret;
/* bandwidth in 10KHz steps */
- ret = si2165_writereg16(state, 0x0308, bw10k);
+ ret = si2165_writereg16(state, REG_T_BANDWIDTH, bw10k);
if (ret < 0)
return ret;
ret = si2165_set_oversamp(state, dvb_rate);
@@ -831,33 +944,30 @@ static int si2165_set_frontend_dvbt(struct dvb_frontend *fe)
static const struct si2165_reg_value_pair dvbc_regs[] = {
/* standard = DVB-C */
- { 0x00ec, 0x05 },
- { 0x08f8, 0x00 },
+ { REG_DVB_STANDARD, 0x05 },
/* agc2 */
- { 0x016e, 0x50 },
- { 0x016c, 0x0e },
- { 0x016d, 0x10 },
+ { REG_AGC2_MIN, 0x50 },
+ { REG_AGC2_KACQ, 0x0e },
+ { REG_AGC2_KLOC, 0x10 },
/* agc */
- { 0x015b, 0x03 },
- { 0x0150, 0x68 },
+ { REG_AGC_UNFREEZE_THR, 0x03 },
+ { REG_AGC_CRESTF_DBX8, 0x68 },
/* agc */
- { 0x01a0, 0x68 },
- { 0x01c8, 0x50 },
-
- { 0x0278, 0x0d },
-
- { 0x023a, 0x05 },
- { 0x0261, 0x09 },
- REG16(0x0350, 0x3e80),
- { 0x02f4, 0x00 },
-
- { 0x00cb, 0x01 },
- REG16(0x024c, 0x0000),
- REG16(0x027c, 0x0000),
- { 0x0232, 0x03 },
- { 0x02f4, 0x0b },
- { 0x018b, 0x00 },
+ { REG_AAF_CRESTF_DBX8, 0x68 },
+ { REG_ACI_CRESTF_DBX8, 0x50 },
+
+ { REG_EQ_AUTO_CONTROL, 0x0d },
+
+ { REG_KP_LOCK, 0x05 },
+ { REG_CENTRAL_TAP, 0x09 },
+ REG16(REG_UNKNOWN_350, 0x3e80),
+
+ { REG_AUTO_RESET, 0x01 },
+ REG16(REG_UNKNOWN_24C, 0x0000),
+ REG16(REG_UNKNOWN_27C, 0x0000),
+ { REG_SWEEP_STEP, 0x03 },
+ { REG_AGC_IF_TRI, 0x00 },
};
static int si2165_set_frontend_dvbc(struct dvb_frontend *fe)
@@ -866,7 +976,7 @@ static int si2165_set_frontend_dvbc(struct dvb_frontend *fe)
int ret;
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
const u32 dvb_rate = p->symbol_rate;
- const u32 bw_hz = p->bandwidth_hz;
+ u8 u8tmp;
if (!state->has_dvbc)
return -EINVAL;
@@ -883,7 +993,32 @@ static int si2165_set_frontend_dvbc(struct dvb_frontend *fe)
if (ret < 0)
return ret;
- ret = si2165_writereg32(state, 0x00c4, bw_hz);
+ switch (p->modulation) {
+ case QPSK:
+ u8tmp = 0x3;
+ break;
+ case QAM_16:
+ u8tmp = 0x7;
+ break;
+ case QAM_32:
+ u8tmp = 0x8;
+ break;
+ case QAM_64:
+ u8tmp = 0x9;
+ break;
+ case QAM_128:
+ u8tmp = 0xa;
+ break;
+ case QAM_256:
+ default:
+ u8tmp = 0xb;
+ break;
+ }
+ ret = si2165_writereg8(state, REG_REQ_CONSTELLATION, u8tmp);
+ if (ret < 0)
+ return ret;
+
+ ret = si2165_writereg32(state, REG_LOCK_TIMEOUT, 0x007a1200);
if (ret < 0)
return ret;
@@ -894,12 +1029,12 @@ static int si2165_set_frontend_dvbc(struct dvb_frontend *fe)
return 0;
}
-static const struct si2165_reg_value_pair agc_rewrite[] = {
- { 0x012a, 0x46 },
- { 0x012c, 0x00 },
- { 0x012e, 0x0a },
- { 0x012f, 0xff },
- { 0x0123, 0x70 }
+static const struct si2165_reg_value_pair adc_rewrite[] = {
+ { REG_ADC_RI1, 0x46 },
+ { REG_ADC_RI3, 0x00 },
+ { REG_ADC_RI5, 0x0a },
+ { REG_ADC_RI6, 0xff },
+ { REG_ADC_RI8, 0x70 }
};
static int si2165_set_frontend(struct dvb_frontend *fe)
@@ -931,7 +1066,7 @@ static int si2165_set_frontend(struct dvb_frontend *fe)
}
/* dsp_addr_jump */
- ret = si2165_writereg32(state, 0x0348, 0xf4000000);
+ ret = si2165_writereg32(state, REG_ADDR_JUMP, 0xf4000000);
if (ret < 0)
return ret;
@@ -944,34 +1079,34 @@ static int si2165_set_frontend(struct dvb_frontend *fe)
return ret;
/* boot/wdog status */
- ret = si2165_readreg8(state, 0x0341, val);
+ ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, val);
if (ret < 0)
return ret;
- ret = si2165_writereg8(state, 0x0341, 0x00);
+ ret = si2165_writereg8(state, REG_WDOG_AND_BOOT, 0x00);
if (ret < 0)
return ret;
/* reset all */
- ret = si2165_writereg8(state, 0x00c0, 0x00);
+ ret = si2165_writereg8(state, REG_RST_ALL, 0x00);
if (ret < 0)
return ret;
/* gp_reg0 */
- ret = si2165_writereg32(state, 0x0384, 0x00000000);
+ ret = si2165_writereg32(state, REG_GP_REG0_LSB, 0x00000000);
if (ret < 0)
return ret;
/* write adc values after each reset*/
- ret = si2165_write_reg_list(state, agc_rewrite,
- ARRAY_SIZE(agc_rewrite));
+ ret = si2165_write_reg_list(state, adc_rewrite,
+ ARRAY_SIZE(adc_rewrite));
if (ret < 0)
return ret;
/* start_synchro */
- ret = si2165_writereg8(state, 0x02e0, 0x01);
+ ret = si2165_writereg8(state, REG_START_SYNCHRO, 0x01);
if (ret < 0)
return ret;
/* boot/wdog status */
- ret = si2165_readreg8(state, 0x0341, val);
+ ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, val);
if (ret < 0)
return ret;
@@ -998,7 +1133,6 @@ static const struct dvb_frontend_ops si2165_ops = {
FE_CAN_QAM_64 |
FE_CAN_QAM_128 |
FE_CAN_QAM_256 |
- FE_CAN_QAM_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO |
FE_CAN_MUTE_TS |
@@ -1013,10 +1147,12 @@ static const struct dvb_frontend_ops si2165_ops = {
.set_frontend = si2165_set_frontend,
.read_status = si2165_read_status,
+ .read_snr = si2165_read_snr,
+ .read_ber = si2165_read_ber,
};
static int si2165_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct si2165_state *state = NULL;
struct si2165_platform_data *pdata = client->dev.platform_data;
@@ -1032,8 +1168,8 @@ static int si2165_probe(struct i2c_client *client,
};
/* allocate memory for the internal state */
- state = kzalloc(sizeof(struct si2165_state), GFP_KERNEL);
- if (state == NULL) {
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state) {
ret = -ENOMEM;
goto error;
}
@@ -1049,45 +1185,45 @@ static int si2165_probe(struct i2c_client *client,
state->client = client;
state->config.i2c_addr = client->addr;
state->config.chip_mode = pdata->chip_mode;
- state->config.ref_freq_Hz = pdata->ref_freq_Hz;
+ state->config.ref_freq_hz = pdata->ref_freq_hz;
state->config.inversion = pdata->inversion;
- if (state->config.ref_freq_Hz < 4000000
- || state->config.ref_freq_Hz > 27000000) {
- dev_err(&state->client->dev, "%s: ref_freq of %d Hz not supported by this driver\n",
- KBUILD_MODNAME, state->config.ref_freq_Hz);
+ if (state->config.ref_freq_hz < 4000000 ||
+ state->config.ref_freq_hz > 27000000) {
+ dev_err(&state->client->dev, "ref_freq of %d Hz not supported by this driver\n",
+ state->config.ref_freq_hz);
ret = -EINVAL;
goto error;
}
/* create dvb_frontend */
memcpy(&state->fe.ops, &si2165_ops,
- sizeof(struct dvb_frontend_ops));
+ sizeof(struct dvb_frontend_ops));
state->fe.ops.release = NULL;
state->fe.demodulator_priv = state;
i2c_set_clientdata(client, state);
/* powerup */
- ret = si2165_writereg8(state, 0x0000, state->config.chip_mode);
+ ret = si2165_writereg8(state, REG_CHIP_MODE, state->config.chip_mode);
if (ret < 0)
goto nodev_error;
- ret = si2165_readreg8(state, 0x0000, &val);
+ ret = si2165_readreg8(state, REG_CHIP_MODE, &val);
if (ret < 0)
goto nodev_error;
if (val != state->config.chip_mode)
goto nodev_error;
- ret = si2165_readreg8(state, 0x0023, &state->chip_revcode);
+ ret = si2165_readreg8(state, REG_CHIP_REVCODE, &state->chip_revcode);
if (ret < 0)
goto nodev_error;
- ret = si2165_readreg8(state, 0x0118, &state->chip_type);
+ ret = si2165_readreg8(state, REV_CHIP_TYPE, &state->chip_type);
if (ret < 0)
goto nodev_error;
/* powerdown */
- ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF);
+ ret = si2165_writereg8(state, REG_CHIP_MODE, SI2165_MODE_OFF);
if (ret < 0)
goto nodev_error;
@@ -1107,18 +1243,18 @@ static int si2165_probe(struct i2c_client *client,
state->has_dvbc = true;
break;
default:
- dev_err(&state->client->dev, "%s: Unsupported Silicon Labs chip (type %d, rev %d)\n",
- KBUILD_MODNAME, state->chip_type, state->chip_revcode);
+ dev_err(&state->client->dev, "Unsupported Silicon Labs chip (type %d, rev %d)\n",
+ state->chip_type, state->chip_revcode);
goto nodev_error;
}
dev_info(&state->client->dev,
- "%s: Detected Silicon Labs %s-%c (type %d, rev %d)\n",
- KBUILD_MODNAME, chip_name, rev_char, state->chip_type,
+ "Detected Silicon Labs %s-%c (type %d, rev %d)\n",
+ chip_name, rev_char, state->chip_type,
state->chip_revcode);
strlcat(state->fe.ops.info.name, chip_name,
- sizeof(state->fe.ops.info.name));
+ sizeof(state->fe.ops.info.name));
n = 0;
if (state->has_dvbt) {
@@ -1173,9 +1309,6 @@ static struct i2c_driver si2165_driver = {
module_i2c_driver(si2165_driver);
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
MODULE_DESCRIPTION("Silicon Labs Si2165 DVB-C/-T Demodulator driver");
MODULE_AUTHOR("Matthias Schwarzott <zzam@gentoo.org>");
MODULE_LICENSE("GPL");