162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci mxb - v4l2 driver for the Multimedia eXtension Board 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci Copyright (C) 1998-2006 Michael Hunold <michael@mihu.de> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html 862306a36Sopenharmony_ci for further details about this card. 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci*/ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define DEBUG_VARIABLE debug 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <media/drv-intf/saa7146_vv.h> 1762306a36Sopenharmony_ci#include <media/tuner.h> 1862306a36Sopenharmony_ci#include <media/v4l2-common.h> 1962306a36Sopenharmony_ci#include <media/i2c/saa7115.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "tea6415c.h" 2462306a36Sopenharmony_ci#include "tea6420.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MXB_AUDIOS 6 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define I2C_SAA7111A 0x24 2962306a36Sopenharmony_ci#define I2C_TDA9840 0x42 3062306a36Sopenharmony_ci#define I2C_TEA6415C 0x43 3162306a36Sopenharmony_ci#define I2C_TEA6420_1 0x4c 3262306a36Sopenharmony_ci#define I2C_TEA6420_2 0x4d 3362306a36Sopenharmony_ci#define I2C_TUNER 0x60 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* global variable */ 3862306a36Sopenharmony_cistatic int mxb_num; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* initial frequence the tuner will be tuned to. 4162306a36Sopenharmony_ci in verden (lower saxony, germany) 4148 is a 4262306a36Sopenharmony_ci channel called "phoenix" */ 4362306a36Sopenharmony_cistatic int freq = 4148; 4462306a36Sopenharmony_cimodule_param(freq, int, 0644); 4562306a36Sopenharmony_ciMODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int debug; 4862306a36Sopenharmony_cimodule_param(debug, int, 0644); 4962306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define MXB_STD (V4L2_STD_PAL_BG | V4L2_STD_PAL_I | V4L2_STD_SECAM | V4L2_STD_NTSC) 5262306a36Sopenharmony_ci#define MXB_INPUTS 4 5362306a36Sopenharmony_cienum { TUNER, AUX1, AUX3, AUX3_YC }; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct v4l2_input mxb_inputs[MXB_INPUTS] = { 5662306a36Sopenharmony_ci { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0, 5762306a36Sopenharmony_ci V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, 5862306a36Sopenharmony_ci { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, 5962306a36Sopenharmony_ci MXB_STD, 0, V4L2_IN_CAP_STD }, 6062306a36Sopenharmony_ci { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, 6162306a36Sopenharmony_ci MXB_STD, 0, V4L2_IN_CAP_STD }, 6262306a36Sopenharmony_ci { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, 6362306a36Sopenharmony_ci MXB_STD, 0, V4L2_IN_CAP_STD }, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* this array holds the information, which port of the saa7146 each 6762306a36Sopenharmony_ci input actually uses. the mxb uses port 0 for every input */ 6862306a36Sopenharmony_cistatic struct { 6962306a36Sopenharmony_ci int hps_source; 7062306a36Sopenharmony_ci int hps_sync; 7162306a36Sopenharmony_ci} input_port_selection[MXB_INPUTS] = { 7262306a36Sopenharmony_ci { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, 7362306a36Sopenharmony_ci { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, 7462306a36Sopenharmony_ci { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, 7562306a36Sopenharmony_ci { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* this array holds the information of the audio source (mxb_audios), 7962306a36Sopenharmony_ci which has to be switched corresponding to the video source (mxb_channels) */ 8062306a36Sopenharmony_cistatic int video_audio_connect[MXB_INPUTS] = 8162306a36Sopenharmony_ci { 0, 1, 3, 3 }; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct mxb_routing { 8462306a36Sopenharmony_ci u32 input; 8562306a36Sopenharmony_ci u32 output; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* these are the available audio sources, which can switched 8962306a36Sopenharmony_ci to the line- and cd-output individually */ 9062306a36Sopenharmony_cistatic struct v4l2_audio mxb_audios[MXB_AUDIOS] = { 9162306a36Sopenharmony_ci { 9262306a36Sopenharmony_ci .index = 0, 9362306a36Sopenharmony_ci .name = "Tuner", 9462306a36Sopenharmony_ci .capability = V4L2_AUDCAP_STEREO, 9562306a36Sopenharmony_ci } , { 9662306a36Sopenharmony_ci .index = 1, 9762306a36Sopenharmony_ci .name = "AUX1", 9862306a36Sopenharmony_ci .capability = V4L2_AUDCAP_STEREO, 9962306a36Sopenharmony_ci } , { 10062306a36Sopenharmony_ci .index = 2, 10162306a36Sopenharmony_ci .name = "AUX2", 10262306a36Sopenharmony_ci .capability = V4L2_AUDCAP_STEREO, 10362306a36Sopenharmony_ci } , { 10462306a36Sopenharmony_ci .index = 3, 10562306a36Sopenharmony_ci .name = "AUX3", 10662306a36Sopenharmony_ci .capability = V4L2_AUDCAP_STEREO, 10762306a36Sopenharmony_ci } , { 10862306a36Sopenharmony_ci .index = 4, 10962306a36Sopenharmony_ci .name = "Radio (X9)", 11062306a36Sopenharmony_ci .capability = V4L2_AUDCAP_STEREO, 11162306a36Sopenharmony_ci } , { 11262306a36Sopenharmony_ci .index = 5, 11362306a36Sopenharmony_ci .name = "CD-ROM (X10)", 11462306a36Sopenharmony_ci .capability = V4L2_AUDCAP_STEREO, 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* These are the necessary input-output-pins for bringing one audio source 11962306a36Sopenharmony_ci (see above) to the CD-output. Note that gain is set to 0 in this table. */ 12062306a36Sopenharmony_cistatic struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { 12162306a36Sopenharmony_ci { { 1, 1 }, { 1, 1 } }, /* Tuner */ 12262306a36Sopenharmony_ci { { 5, 1 }, { 6, 1 } }, /* AUX 1 */ 12362306a36Sopenharmony_ci { { 4, 1 }, { 6, 1 } }, /* AUX 2 */ 12462306a36Sopenharmony_ci { { 3, 1 }, { 6, 1 } }, /* AUX 3 */ 12562306a36Sopenharmony_ci { { 1, 1 }, { 3, 1 } }, /* Radio */ 12662306a36Sopenharmony_ci { { 1, 1 }, { 2, 1 } }, /* CD-Rom */ 12762306a36Sopenharmony_ci { { 6, 1 }, { 6, 1 } } /* Mute */ 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* These are the necessary input-output-pins for bringing one audio source 13162306a36Sopenharmony_ci (see above) to the line-output. Note that gain is set to 0 in this table. */ 13262306a36Sopenharmony_cistatic struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { 13362306a36Sopenharmony_ci { { 2, 3 }, { 1, 2 } }, 13462306a36Sopenharmony_ci { { 5, 3 }, { 6, 2 } }, 13562306a36Sopenharmony_ci { { 4, 3 }, { 6, 2 } }, 13662306a36Sopenharmony_ci { { 3, 3 }, { 6, 2 } }, 13762306a36Sopenharmony_ci { { 2, 3 }, { 3, 2 } }, 13862306a36Sopenharmony_ci { { 2, 3 }, { 2, 2 } }, 13962306a36Sopenharmony_ci { { 6, 3 }, { 6, 2 } } /* Mute */ 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistruct mxb 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct video_device video_dev; 14562306a36Sopenharmony_ci struct video_device vbi_dev; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci struct i2c_adapter i2c_adapter; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci struct v4l2_subdev *saa7111a; 15062306a36Sopenharmony_ci struct v4l2_subdev *tda9840; 15162306a36Sopenharmony_ci struct v4l2_subdev *tea6415c; 15262306a36Sopenharmony_ci struct v4l2_subdev *tuner; 15362306a36Sopenharmony_ci struct v4l2_subdev *tea6420_1; 15462306a36Sopenharmony_ci struct v4l2_subdev *tea6420_2; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci int cur_mode; /* current audio mode (mono, stereo, ...) */ 15762306a36Sopenharmony_ci int cur_input; /* current input */ 15862306a36Sopenharmony_ci int cur_audinput; /* current audio input */ 15962306a36Sopenharmony_ci int cur_mute; /* current mute status */ 16062306a36Sopenharmony_ci struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci#define saa7111a_call(mxb, o, f, args...) \ 16462306a36Sopenharmony_ci v4l2_subdev_call(mxb->saa7111a, o, f, ##args) 16562306a36Sopenharmony_ci#define tda9840_call(mxb, o, f, args...) \ 16662306a36Sopenharmony_ci v4l2_subdev_call(mxb->tda9840, o, f, ##args) 16762306a36Sopenharmony_ci#define tea6415c_call(mxb, o, f, args...) \ 16862306a36Sopenharmony_ci v4l2_subdev_call(mxb->tea6415c, o, f, ##args) 16962306a36Sopenharmony_ci#define tuner_call(mxb, o, f, args...) \ 17062306a36Sopenharmony_ci v4l2_subdev_call(mxb->tuner, o, f, ##args) 17162306a36Sopenharmony_ci#define call_all(dev, o, f, args...) \ 17262306a36Sopenharmony_ci v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void mxb_update_audmode(struct mxb *mxb) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct v4l2_tuner t = { 17762306a36Sopenharmony_ci .audmode = mxb->cur_mode, 17862306a36Sopenharmony_ci }; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci tda9840_call(mxb, tuner, s_tuner, &t); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline void tea6420_route(struct mxb *mxb, int idx) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, 18662306a36Sopenharmony_ci TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); 18762306a36Sopenharmony_ci v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, 18862306a36Sopenharmony_ci TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); 18962306a36Sopenharmony_ci v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, 19062306a36Sopenharmony_ci TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); 19162306a36Sopenharmony_ci v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, 19262306a36Sopenharmony_ci TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic struct saa7146_extension extension; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int mxb_s_ctrl(struct v4l2_ctrl *ctrl) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct saa7146_dev *dev = container_of(ctrl->handler, 20062306a36Sopenharmony_ci struct saa7146_dev, ctrl_handler); 20162306a36Sopenharmony_ci struct mxb *mxb = dev->ext_priv; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci switch (ctrl->id) { 20462306a36Sopenharmony_ci case V4L2_CID_AUDIO_MUTE: 20562306a36Sopenharmony_ci mxb->cur_mute = ctrl->val; 20662306a36Sopenharmony_ci /* switch the audio-source */ 20762306a36Sopenharmony_ci tea6420_route(mxb, ctrl->val ? 6 : 20862306a36Sopenharmony_ci video_audio_connect[mxb->cur_input]); 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci default: 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops mxb_ctrl_ops = { 21762306a36Sopenharmony_ci .s_ctrl = mxb_s_ctrl, 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int mxb_probe(struct saa7146_dev *dev) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; 22362306a36Sopenharmony_ci struct mxb *mxb = NULL; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, 22662306a36Sopenharmony_ci V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 22762306a36Sopenharmony_ci if (hdl->error) 22862306a36Sopenharmony_ci return hdl->error; 22962306a36Sopenharmony_ci mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); 23062306a36Sopenharmony_ci if (mxb == NULL) { 23162306a36Sopenharmony_ci DEB_D("not enough kernel memory\n"); 23262306a36Sopenharmony_ci return -ENOMEM; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); 23962306a36Sopenharmony_ci if (i2c_add_adapter(&mxb->i2c_adapter) < 0) { 24062306a36Sopenharmony_ci DEB_S("cannot register i2c-device. skipping.\n"); 24162306a36Sopenharmony_ci kfree(mxb); 24262306a36Sopenharmony_ci return -EFAULT; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, 24662306a36Sopenharmony_ci "saa7111", I2C_SAA7111A, NULL); 24762306a36Sopenharmony_ci mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, 24862306a36Sopenharmony_ci "tea6420", I2C_TEA6420_1, NULL); 24962306a36Sopenharmony_ci mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, 25062306a36Sopenharmony_ci "tea6420", I2C_TEA6420_2, NULL); 25162306a36Sopenharmony_ci mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, 25262306a36Sopenharmony_ci "tea6415c", I2C_TEA6415C, NULL); 25362306a36Sopenharmony_ci mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, 25462306a36Sopenharmony_ci "tda9840", I2C_TDA9840, NULL); 25562306a36Sopenharmony_ci mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, 25662306a36Sopenharmony_ci "tuner", I2C_TUNER, NULL); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* check if all devices are present */ 25962306a36Sopenharmony_ci if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || 26062306a36Sopenharmony_ci !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) { 26162306a36Sopenharmony_ci pr_err("did not find all i2c devices. aborting\n"); 26262306a36Sopenharmony_ci i2c_del_adapter(&mxb->i2c_adapter); 26362306a36Sopenharmony_ci kfree(mxb); 26462306a36Sopenharmony_ci return -ENODEV; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* all devices are present, probe was successful */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* we store the pointer in our private data field */ 27062306a36Sopenharmony_ci dev->ext_priv = mxb; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci v4l2_ctrl_handler_setup(hdl); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* some init data for the saa7740, the so-called 'sound arena module'. 27862306a36Sopenharmony_ci there are no specs available, so we simply use some init values */ 27962306a36Sopenharmony_cistatic struct { 28062306a36Sopenharmony_ci int length; 28162306a36Sopenharmony_ci char data[9]; 28262306a36Sopenharmony_ci} mxb_saa7740_init[] = { 28362306a36Sopenharmony_ci { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } }, 28462306a36Sopenharmony_ci { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } }, 28562306a36Sopenharmony_ci { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } }, 28662306a36Sopenharmony_ci { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } }, 28762306a36Sopenharmony_ci { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } }, 28862306a36Sopenharmony_ci { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } }, 28962306a36Sopenharmony_ci { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } }, 29062306a36Sopenharmony_ci { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } }, 29162306a36Sopenharmony_ci { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } }, 29262306a36Sopenharmony_ci { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } }, 29362306a36Sopenharmony_ci { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } }, 29462306a36Sopenharmony_ci { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } }, 29562306a36Sopenharmony_ci { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } }, 29662306a36Sopenharmony_ci { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } }, 29762306a36Sopenharmony_ci { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } }, 29862306a36Sopenharmony_ci { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } }, 29962306a36Sopenharmony_ci { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } }, 30062306a36Sopenharmony_ci { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } }, 30162306a36Sopenharmony_ci { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } }, 30262306a36Sopenharmony_ci { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } }, 30362306a36Sopenharmony_ci { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } }, 30462306a36Sopenharmony_ci { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } }, 30562306a36Sopenharmony_ci { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } }, 30662306a36Sopenharmony_ci { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } }, 30762306a36Sopenharmony_ci { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } }, 30862306a36Sopenharmony_ci { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } }, 30962306a36Sopenharmony_ci { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } }, 31062306a36Sopenharmony_ci { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } }, 31162306a36Sopenharmony_ci { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } }, 31262306a36Sopenharmony_ci { 3, { 0x48, 0x00, 0x01 } }, 31362306a36Sopenharmony_ci { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, 31462306a36Sopenharmony_ci { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, 31562306a36Sopenharmony_ci { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, 31662306a36Sopenharmony_ci { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, 31762306a36Sopenharmony_ci { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, 31862306a36Sopenharmony_ci { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, 31962306a36Sopenharmony_ci { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, 32062306a36Sopenharmony_ci { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, 32162306a36Sopenharmony_ci { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, 32262306a36Sopenharmony_ci { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, 32362306a36Sopenharmony_ci { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, 32462306a36Sopenharmony_ci { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, 32562306a36Sopenharmony_ci { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, 32662306a36Sopenharmony_ci { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, 32762306a36Sopenharmony_ci { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, 32862306a36Sopenharmony_ci { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, 32962306a36Sopenharmony_ci { 3, { 0x80, 0xb3, 0x0a } }, 33062306a36Sopenharmony_ci {-1, { 0 } } 33162306a36Sopenharmony_ci}; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* bring hardware to a sane state. this has to be done, just in case someone 33462306a36Sopenharmony_ci wants to capture from this device before it has been properly initialized. 33562306a36Sopenharmony_ci the capture engine would badly fail, because no valid signal arrives on the 33662306a36Sopenharmony_ci saa7146, thus leading to timeouts and stuff. */ 33762306a36Sopenharmony_cistatic int mxb_init_done(struct saa7146_dev* dev) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct mxb* mxb = (struct mxb*)dev->ext_priv; 34062306a36Sopenharmony_ci struct i2c_msg msg; 34162306a36Sopenharmony_ci struct tuner_setup tun_setup; 34262306a36Sopenharmony_ci v4l2_std_id std = V4L2_STD_PAL_BG; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci int i, err = 0; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* mute audio on tea6420s */ 34762306a36Sopenharmony_ci tea6420_route(mxb, 6); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* select video mode in saa7111a */ 35062306a36Sopenharmony_ci saa7111a_call(mxb, video, s_std, std); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* select tuner-output on saa7111a */ 35362306a36Sopenharmony_ci saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0, 35462306a36Sopenharmony_ci SAA7111_FMT_CCIR, 0); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* select a tuner type */ 35762306a36Sopenharmony_ci tun_setup.mode_mask = T_ANALOG_TV; 35862306a36Sopenharmony_ci tun_setup.addr = ADDR_UNSET; 35962306a36Sopenharmony_ci tun_setup.type = TUNER_PHILIPS_PAL; 36062306a36Sopenharmony_ci tuner_call(mxb, tuner, s_type_addr, &tun_setup); 36162306a36Sopenharmony_ci /* tune in some frequency on tuner */ 36262306a36Sopenharmony_ci mxb->cur_freq.tuner = 0; 36362306a36Sopenharmony_ci mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; 36462306a36Sopenharmony_ci mxb->cur_freq.frequency = freq; 36562306a36Sopenharmony_ci tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* set a default video standard */ 36862306a36Sopenharmony_ci /* These two gpio calls set the GPIO pins that control the tda9820 */ 36962306a36Sopenharmony_ci saa7146_write(dev, GPIO_CTRL, 0x00404050); 37062306a36Sopenharmony_ci saa7111a_call(mxb, core, s_gpio, 1); 37162306a36Sopenharmony_ci saa7111a_call(mxb, video, s_std, std); 37262306a36Sopenharmony_ci tuner_call(mxb, video, s_std, std); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* switch to tuner-channel on tea6415c */ 37562306a36Sopenharmony_ci tea6415c_call(mxb, video, s_routing, 3, 17, 0); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* select tuner-output on multicable on tea6415c */ 37862306a36Sopenharmony_ci tea6415c_call(mxb, video, s_routing, 3, 13, 0); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* the rest for mxb */ 38162306a36Sopenharmony_ci mxb->cur_input = 0; 38262306a36Sopenharmony_ci mxb->cur_audinput = video_audio_connect[mxb->cur_input]; 38362306a36Sopenharmony_ci mxb->cur_mute = 1; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci mxb->cur_mode = V4L2_TUNER_MODE_STEREO; 38662306a36Sopenharmony_ci mxb_update_audmode(mxb); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* check if the saa7740 (aka 'sound arena module') is present 38962306a36Sopenharmony_ci on the mxb. if so, we must initialize it. due to lack of 39062306a36Sopenharmony_ci information about the saa7740, the values were reverse 39162306a36Sopenharmony_ci engineered. */ 39262306a36Sopenharmony_ci msg.addr = 0x1b; 39362306a36Sopenharmony_ci msg.flags = 0; 39462306a36Sopenharmony_ci msg.len = mxb_saa7740_init[0].length; 39562306a36Sopenharmony_ci msg.buf = &mxb_saa7740_init[0].data[0]; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); 39862306a36Sopenharmony_ci if (err == 1) { 39962306a36Sopenharmony_ci /* the sound arena module is a pos, that's probably the reason 40062306a36Sopenharmony_ci philips refuses to hand out a datasheet for the saa7740... 40162306a36Sopenharmony_ci it seems to screw up the i2c bus, so we disable fast irq 40262306a36Sopenharmony_ci based i2c transactions here and rely on the slow and safe 40362306a36Sopenharmony_ci polling method ... */ 40462306a36Sopenharmony_ci extension.flags &= ~SAA7146_USE_I2C_IRQ; 40562306a36Sopenharmony_ci for (i = 1; ; i++) { 40662306a36Sopenharmony_ci if (-1 == mxb_saa7740_init[i].length) 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci msg.len = mxb_saa7740_init[i].length; 41062306a36Sopenharmony_ci msg.buf = &mxb_saa7740_init[i].data[0]; 41162306a36Sopenharmony_ci err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); 41262306a36Sopenharmony_ci if (err != 1) { 41362306a36Sopenharmony_ci DEB_D("failed to initialize 'sound arena module'\n"); 41462306a36Sopenharmony_ci goto err; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci pr_info("'sound arena module' detected\n"); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_cierr: 42062306a36Sopenharmony_ci /* the rest for saa7146: you should definitely set some basic values 42162306a36Sopenharmony_ci for the input-port handling of the saa7146. */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* ext->saa has been filled by the core driver */ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* some stuff is done via variables */ 42662306a36Sopenharmony_ci saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, 42762306a36Sopenharmony_ci input_port_selection[mxb->cur_input].hps_sync); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* some stuff is done via direct write to the registers */ 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* this is ugly, but because of the fact that this is completely 43262306a36Sopenharmony_ci hardware dependend, it should be done directly... */ 43362306a36Sopenharmony_ci saa7146_write(dev, DD1_STREAM_B, 0x00000000); 43462306a36Sopenharmony_ci saa7146_write(dev, DD1_INIT, 0x02000200); 43562306a36Sopenharmony_ci saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* interrupt-handler. this gets called when irq_mask is != 0. 44162306a36Sopenharmony_ci it must clear the interrupt-bits in irq_mask it has handled */ 44262306a36Sopenharmony_ci/* 44362306a36Sopenharmony_civoid mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct mxb* mxb = (struct mxb*)dev->ext_priv; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci*/ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); 45262306a36Sopenharmony_ci if (i->index >= MXB_INPUTS) 45362306a36Sopenharmony_ci return -EINVAL; 45462306a36Sopenharmony_ci memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *fh, unsigned int *i) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 46162306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 46262306a36Sopenharmony_ci *i = mxb->cur_input; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci DEB_EE("VIDIOC_G_INPUT %d\n", *i); 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *fh, unsigned int input) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 47162306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 47262306a36Sopenharmony_ci int err = 0; 47362306a36Sopenharmony_ci int i = 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci DEB_EE("VIDIOC_S_INPUT %d\n", input); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (input >= MXB_INPUTS) 47862306a36Sopenharmony_ci return -EINVAL; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci mxb->cur_input = input; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, 48362306a36Sopenharmony_ci input_port_selection[input].hps_sync); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* prepare switching of tea6415c and saa7111a; 48662306a36Sopenharmony_ci have a look at the 'background'-file for further information */ 48762306a36Sopenharmony_ci switch (input) { 48862306a36Sopenharmony_ci case TUNER: 48962306a36Sopenharmony_ci i = SAA7115_COMPOSITE0; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci err = tea6415c_call(mxb, video, s_routing, 3, 17, 0); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* connect tuner-output always to multicable */ 49462306a36Sopenharmony_ci if (!err) 49562306a36Sopenharmony_ci err = tea6415c_call(mxb, video, s_routing, 3, 13, 0); 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case AUX3_YC: 49862306a36Sopenharmony_ci /* nothing to be done here. aux3_yc is 49962306a36Sopenharmony_ci directly connected to the saa711a */ 50062306a36Sopenharmony_ci i = SAA7115_SVIDEO1; 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case AUX3: 50362306a36Sopenharmony_ci /* nothing to be done here. aux3 is 50462306a36Sopenharmony_ci directly connected to the saa711a */ 50562306a36Sopenharmony_ci i = SAA7115_COMPOSITE1; 50662306a36Sopenharmony_ci break; 50762306a36Sopenharmony_ci case AUX1: 50862306a36Sopenharmony_ci i = SAA7115_COMPOSITE0; 50962306a36Sopenharmony_ci err = tea6415c_call(mxb, video, s_routing, 1, 17, 0); 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (err) 51462306a36Sopenharmony_ci return err; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci mxb->video_dev.tvnorms = mxb_inputs[input].std; 51762306a36Sopenharmony_ci mxb->vbi_dev.tvnorms = mxb_inputs[input].std; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* switch video in saa7111a */ 52062306a36Sopenharmony_ci if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) 52162306a36Sopenharmony_ci pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mxb->cur_audinput = video_audio_connect[input]; 52462306a36Sopenharmony_ci /* switch the audio-source only if necessary */ 52562306a36Sopenharmony_ci if (0 == mxb->cur_mute) 52662306a36Sopenharmony_ci tea6420_route(mxb, mxb->cur_audinput); 52762306a36Sopenharmony_ci if (mxb->cur_audinput == 0) 52862306a36Sopenharmony_ci mxb_update_audmode(mxb); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 53662306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (t->index) { 53962306a36Sopenharmony_ci DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n", 54062306a36Sopenharmony_ci t->index); 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci DEB_EE("VIDIOC_G_TUNER: %d\n", t->index); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci memset(t, 0, sizeof(*t)); 54762306a36Sopenharmony_ci strscpy(t->name, "TV Tuner", sizeof(t->name)); 54862306a36Sopenharmony_ci t->type = V4L2_TUNER_ANALOG_TV; 54962306a36Sopenharmony_ci t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | 55062306a36Sopenharmony_ci V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; 55162306a36Sopenharmony_ci t->audmode = mxb->cur_mode; 55262306a36Sopenharmony_ci return call_all(dev, tuner, g_tuner, t); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 55862306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (t->index) { 56162306a36Sopenharmony_ci DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n", 56262306a36Sopenharmony_ci t->index); 56362306a36Sopenharmony_ci return -EINVAL; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci mxb->cur_mode = t->audmode; 56762306a36Sopenharmony_ci return call_all(dev, tuner, s_tuner, t); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return call_all(dev, video, querystd, norm); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 58062306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (f->tuner) 58362306a36Sopenharmony_ci return -EINVAL; 58462306a36Sopenharmony_ci *f = mxb->cur_freq; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 59362306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (f->tuner) 59662306a36Sopenharmony_ci return -EINVAL; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (V4L2_TUNER_ANALOG_TV != f->type) 59962306a36Sopenharmony_ci return -EINVAL; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* tune in desired frequency */ 60462306a36Sopenharmony_ci tuner_call(mxb, tuner, s_frequency, f); 60562306a36Sopenharmony_ci /* let the tuner subdev clamp the frequency to the tuner range */ 60662306a36Sopenharmony_ci mxb->cur_freq = *f; 60762306a36Sopenharmony_ci tuner_call(mxb, tuner, g_frequency, &mxb->cur_freq); 60862306a36Sopenharmony_ci if (mxb->cur_audinput == 0) 60962306a36Sopenharmony_ci mxb_update_audmode(mxb); 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci if (a->index >= MXB_AUDIOS) 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci *a = mxb_audios[a->index]; 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 62462306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci DEB_EE("VIDIOC_G_AUDIO\n"); 62762306a36Sopenharmony_ci *a = mxb_audios[mxb->cur_audinput]; 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 63462306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci DEB_D("VIDIOC_S_AUDIO %d\n", a->index); 63762306a36Sopenharmony_ci if (a->index >= 32 || 63862306a36Sopenharmony_ci !(mxb_inputs[mxb->cur_input].audioset & (1 << a->index))) 63962306a36Sopenharmony_ci return -EINVAL; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (mxb->cur_audinput != a->index) { 64262306a36Sopenharmony_ci mxb->cur_audinput = a->index; 64362306a36Sopenharmony_ci tea6420_route(mxb, a->index); 64462306a36Sopenharmony_ci if (mxb->cur_audinput == 0) 64562306a36Sopenharmony_ci mxb_update_audmode(mxb); 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 65162306a36Sopenharmony_cistatic int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (reg->reg > pci_resource_len(dev->pci, 0) - 4) 65662306a36Sopenharmony_ci return -EINVAL; 65762306a36Sopenharmony_ci reg->val = saa7146_read(dev, reg->reg); 65862306a36Sopenharmony_ci reg->size = 4; 65962306a36Sopenharmony_ci return 0; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int vidioc_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct saa7146_dev *dev = video_drvdata(file); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (reg->reg > pci_resource_len(dev->pci, 0) - 4) 66762306a36Sopenharmony_ci return -EINVAL; 66862306a36Sopenharmony_ci saa7146_write(dev, reg->reg, reg->val); 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci#endif 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic struct saa7146_ext_vv vv_data; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci/* this function only gets called when the probing was successful */ 67662306a36Sopenharmony_cistatic int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct mxb *mxb; 67962306a36Sopenharmony_ci int ret; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci DEB_EE("dev:%p\n", dev); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci ret = saa7146_vv_init(dev, &vv_data); 68462306a36Sopenharmony_ci if (ret) { 68562306a36Sopenharmony_ci ERR("Error in saa7146_vv_init()"); 68662306a36Sopenharmony_ci return ret; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (mxb_probe(dev)) { 69062306a36Sopenharmony_ci saa7146_vv_release(dev); 69162306a36Sopenharmony_ci return -1; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci mxb = (struct mxb *)dev->ext_priv; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; 69662306a36Sopenharmony_ci vv_data.vid_ops.vidioc_g_input = vidioc_g_input; 69762306a36Sopenharmony_ci vv_data.vid_ops.vidioc_s_input = vidioc_s_input; 69862306a36Sopenharmony_ci vv_data.vid_ops.vidioc_querystd = vidioc_querystd; 69962306a36Sopenharmony_ci vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; 70062306a36Sopenharmony_ci vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; 70162306a36Sopenharmony_ci vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; 70262306a36Sopenharmony_ci vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; 70362306a36Sopenharmony_ci vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; 70462306a36Sopenharmony_ci vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; 70562306a36Sopenharmony_ci vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; 70662306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 70762306a36Sopenharmony_ci vv_data.vid_ops.vidioc_g_register = vidioc_g_register; 70862306a36Sopenharmony_ci vv_data.vid_ops.vidioc_s_register = vidioc_s_register; 70962306a36Sopenharmony_ci#endif 71062306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_enum_input = vidioc_enum_input; 71162306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_g_input = vidioc_g_input; 71262306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_s_input = vidioc_s_input; 71362306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_querystd = vidioc_querystd; 71462306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_g_tuner = vidioc_g_tuner; 71562306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_s_tuner = vidioc_s_tuner; 71662306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_g_frequency = vidioc_g_frequency; 71762306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_s_frequency = vidioc_s_frequency; 71862306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_enumaudio = vidioc_enumaudio; 71962306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_g_audio = vidioc_g_audio; 72062306a36Sopenharmony_ci vv_data.vbi_ops.vidioc_s_audio = vidioc_s_audio; 72162306a36Sopenharmony_ci if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_VIDEO)) { 72262306a36Sopenharmony_ci ERR("cannot register capture v4l2 device. skipping.\n"); 72362306a36Sopenharmony_ci saa7146_vv_release(dev); 72462306a36Sopenharmony_ci return -1; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ 72862306a36Sopenharmony_ci if (MXB_BOARD_CAN_DO_VBI(dev)) { 72962306a36Sopenharmony_ci if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { 73062306a36Sopenharmony_ci ERR("cannot register vbi v4l2 device. skipping.\n"); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci pr_info("found Multimedia eXtension Board #%d\n", mxb_num); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci mxb_num++; 73762306a36Sopenharmony_ci mxb_init_done(dev); 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic int mxb_detach(struct saa7146_dev *dev) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci DEB_EE("dev:%p\n", dev); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* mute audio on tea6420s */ 74862306a36Sopenharmony_ci tea6420_route(mxb, 6); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci saa7146_unregister_device(&mxb->video_dev,dev); 75162306a36Sopenharmony_ci if (MXB_BOARD_CAN_DO_VBI(dev)) 75262306a36Sopenharmony_ci saa7146_unregister_device(&mxb->vbi_dev, dev); 75362306a36Sopenharmony_ci saa7146_vv_release(dev); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci mxb_num--; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci i2c_del_adapter(&mxb->i2c_adapter); 75862306a36Sopenharmony_ci kfree(mxb); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci return 0; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct mxb *mxb = (struct mxb *)dev->ext_priv; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (V4L2_STD_PAL_I == standard->id) { 76862306a36Sopenharmony_ci v4l2_std_id std = V4L2_STD_PAL_I; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); 77162306a36Sopenharmony_ci /* These two gpio calls set the GPIO pins that control the tda9820 */ 77262306a36Sopenharmony_ci saa7146_write(dev, GPIO_CTRL, 0x00404050); 77362306a36Sopenharmony_ci saa7111a_call(mxb, core, s_gpio, 0); 77462306a36Sopenharmony_ci saa7111a_call(mxb, video, s_std, std); 77562306a36Sopenharmony_ci if (mxb->cur_input == 0) 77662306a36Sopenharmony_ci tuner_call(mxb, video, s_std, std); 77762306a36Sopenharmony_ci } else { 77862306a36Sopenharmony_ci v4l2_std_id std = V4L2_STD_PAL_BG; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (mxb->cur_input) 78162306a36Sopenharmony_ci std = standard->id; 78262306a36Sopenharmony_ci DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); 78362306a36Sopenharmony_ci /* These two gpio calls set the GPIO pins that control the tda9820 */ 78462306a36Sopenharmony_ci saa7146_write(dev, GPIO_CTRL, 0x00404050); 78562306a36Sopenharmony_ci saa7111a_call(mxb, core, s_gpio, 1); 78662306a36Sopenharmony_ci saa7111a_call(mxb, video, s_std, std); 78762306a36Sopenharmony_ci if (mxb->cur_input == 0) 78862306a36Sopenharmony_ci tuner_call(mxb, video, s_std, std); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic struct saa7146_standard standard[] = { 79462306a36Sopenharmony_ci { 79562306a36Sopenharmony_ci .name = "PAL-BG", .id = V4L2_STD_PAL_BG, 79662306a36Sopenharmony_ci .v_offset = 0x17, .v_field = 288, 79762306a36Sopenharmony_ci .h_offset = 0x14, .h_pixels = 680, 79862306a36Sopenharmony_ci .v_max_out = 576, .h_max_out = 768, 79962306a36Sopenharmony_ci }, { 80062306a36Sopenharmony_ci .name = "PAL-I", .id = V4L2_STD_PAL_I, 80162306a36Sopenharmony_ci .v_offset = 0x17, .v_field = 288, 80262306a36Sopenharmony_ci .h_offset = 0x14, .h_pixels = 680, 80362306a36Sopenharmony_ci .v_max_out = 576, .h_max_out = 768, 80462306a36Sopenharmony_ci }, { 80562306a36Sopenharmony_ci .name = "NTSC", .id = V4L2_STD_NTSC, 80662306a36Sopenharmony_ci .v_offset = 0x16, .v_field = 240, 80762306a36Sopenharmony_ci .h_offset = 0x06, .h_pixels = 708, 80862306a36Sopenharmony_ci .v_max_out = 480, .h_max_out = 640, 80962306a36Sopenharmony_ci }, { 81062306a36Sopenharmony_ci .name = "SECAM", .id = V4L2_STD_SECAM, 81162306a36Sopenharmony_ci .v_offset = 0x14, .v_field = 288, 81262306a36Sopenharmony_ci .h_offset = 0x14, .h_pixels = 720, 81362306a36Sopenharmony_ci .v_max_out = 576, .h_max_out = 768, 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci}; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic struct saa7146_pci_extension_data mxb = { 81862306a36Sopenharmony_ci .ext_priv = "Multimedia eXtension Board", 81962306a36Sopenharmony_ci .ext = &extension, 82062306a36Sopenharmony_ci}; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic const struct pci_device_id pci_tbl[] = { 82362306a36Sopenharmony_ci { 82462306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PHILIPS, 82562306a36Sopenharmony_ci .device = PCI_DEVICE_ID_PHILIPS_SAA7146, 82662306a36Sopenharmony_ci .subvendor = 0x0000, 82762306a36Sopenharmony_ci .subdevice = 0x0000, 82862306a36Sopenharmony_ci .driver_data = (unsigned long)&mxb, 82962306a36Sopenharmony_ci }, { 83062306a36Sopenharmony_ci .vendor = 0, 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci}; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_tbl); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic struct saa7146_ext_vv vv_data = { 83762306a36Sopenharmony_ci .inputs = MXB_INPUTS, 83862306a36Sopenharmony_ci .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, 83962306a36Sopenharmony_ci .stds = &standard[0], 84062306a36Sopenharmony_ci .num_stds = ARRAY_SIZE(standard), 84162306a36Sopenharmony_ci .std_callback = &std_callback, 84262306a36Sopenharmony_ci}; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic struct saa7146_extension extension = { 84562306a36Sopenharmony_ci .name = "Multimedia eXtension Board", 84662306a36Sopenharmony_ci .flags = SAA7146_USE_I2C_IRQ, 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci .pci_tbl = &pci_tbl[0], 84962306a36Sopenharmony_ci .module = THIS_MODULE, 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci .attach = mxb_attach, 85262306a36Sopenharmony_ci .detach = mxb_detach, 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci .irq_mask = 0, 85562306a36Sopenharmony_ci .irq_func = NULL, 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic int __init mxb_init_module(void) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci if (saa7146_register_extension(&extension)) { 86162306a36Sopenharmony_ci DEB_S("failed to register extension\n"); 86262306a36Sopenharmony_ci return -ENODEV; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic void __exit mxb_cleanup_module(void) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci saa7146_unregister_extension(&extension); 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cimodule_init(mxb_init_module); 87462306a36Sopenharmony_cimodule_exit(mxb_cleanup_module); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ciMODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'"); 87762306a36Sopenharmony_ciMODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); 87862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 879