/*
* Programming the mspx4xx sound processor family
*
* (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
*
* what works and what doesn't:
*
* AM-Mono
* Support for Hauppauge cards added (decoding handled by tuner) added by
* Frederic Crozat <fcrozat@mail.dotcom.fr>
*
* FM-Mono
* should work. The stereo modes are backward compatible to FM-mono,
* therefore FM-Mono should be allways available.
*
* FM-Stereo (B/G, used in germany)
* should work, with autodetect
*
* FM-Stereo (satellite)
* should work, no autodetect (i.e. default is mono, but you can
* switch to stereo -- untested)
*
* NICAM (B/G, L , used in UK, Scandinavia, Spain and France)
* should work, with autodetect. Support for NICAM was added by
* Pekka Pietikainen <pp@netppl.fi>
*
* TODO:
* - better SAT support
*
* 980623 Thomas Sailer (sailer@ife.ee.ethz.ch)
* using soundcore instead of OSS
*
* 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 the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/tvaudio.h>
#include <media/msp3400.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include "msp3400-driver.h"
/* ---------------------------------------------------------------------- */
MODULE_DESCRIPTION("device driver for msp34xx TV sound processor");
MODULE_AUTHOR("Gerd Knorr");
MODULE_LICENSE("GPL");
/* module parameters */
static int opmode = OPMODE_AUTO;
int msp_debug; /* msp_debug output */
int msp_once; /* no continous stereo monitoring */
int msp_amsound; /* hard-wire AM sound at 6.5 Hz (france),
the autoscan seems work well only with FM... */
int msp_standard = 1; /* Override auto detect of audio msp_standard, if needed. */
int msp_dolby;
int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual
(msp34xxg only) 0x00a0-0x03c0 */
/* read-only */
module_param(opmode, int, 0444);
/* read-write */
module_param_named(once,msp_once, bool, 0644);
module_param_named(debug,msp_debug, int, 0644);
module_param_named(stereo_threshold,msp_stereo_thresh, int, 0644);
module_param_named(standard,msp_standard, int, 0644);
module_param_named(amsound,msp_amsound, bool, 0644);
module_param_named(dolby,msp_dolby, bool, 0644);
MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect");
MODULE_PARM_DESC(once, "No continuous stereo monitoring");
MODULE_PARM_DESC(debug, "Enable debug messages [0-3]");
MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo");
MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect");
MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan");
MODULE_PARM_DESC(dolby, "Activates Dolby processsing");
/* ---------------------------------------------------------------------- */
/* control subaddress */
#define I2C_MSP_CONTROL 0x00
/* demodulator unit subaddress */
#define I2C_MSP_DEM 0x10
/* DSP unit subaddress */
#define I2C_MSP_DSP 0x12
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x80 >> 1, 0x88 >> 1, I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
/* functions for talking to the MSP3400C Sound processor */
int msp_reset(struct i2c_client *client)
{
/* reset and read revision code */
static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 };
static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 };
static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e };
u8 read[2];
struct i2c_msg reset[2] = {
{ client->addr, I2C_M_IGNORE_NAK, 3, reset_off },
{ client->addr, I2C_M_IGNORE_NAK, 3, reset_on },
};
struct i2c_msg test[2] = {
{ client->addr, 0, 3, write },
{ client->addr, I2C_M_RD, 2, read },
};
v4l_dbg(3, msp_debug, client, "msp_reset\n");
if (i2c_transfer(client->adapter, &reset[0], 1) != 1 ||
i2c_transfer(client->adapter, &reset[1], 1) != 1 ||
i2c_transfer(client->adapter, test, 2) != 2) {
v4l_err(client, "chip reset failed\n");
return -1;
}
return 0;
}
static int msp_read(struct i2c_client *client, int dev, int addr)
{
int err, retval;
u8 write[3];
u8 read[2];
struct i2c_msg msgs[2] = {
{ client->addr, 0, 3, write },
{