mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-30 07:38:52 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
8
drivers/media/i2c/cx25840/Kconfig
Normal file
8
drivers/media/i2c/cx25840/Kconfig
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
config VIDEO_CX25840
|
||||
tristate "Conexant CX2584x audio/video decoders"
|
||||
depends on VIDEO_V4L2 && I2C
|
||||
---help---
|
||||
Support for the Conexant CX2584x audio/video decoders.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cx25840
|
||||
6
drivers/media/i2c/cx25840/Makefile
Normal file
6
drivers/media/i2c/cx25840/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
|
||||
cx25840-vbi.o cx25840-ir.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
|
||||
|
||||
ccflags-y += -Idrivers/media/i2c
|
||||
571
drivers/media/i2c/cx25840/cx25840-audio.c
Normal file
571
drivers/media/i2c/cx25840/cx25840-audio.c
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
/* cx25840 audio functions
|
||||
*
|
||||
* 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/videodev2.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/cx25840.h>
|
||||
|
||||
#include "cx25840-core.h"
|
||||
|
||||
/*
|
||||
* Note: The PLL and SRC parameters are based on a reference frequency that
|
||||
* would ideally be:
|
||||
*
|
||||
* NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
|
||||
*
|
||||
* However, it's not the exact reference frequency that matters, only that the
|
||||
* firmware and modules that comprise the driver for a particular board all
|
||||
* use the same value (close to the ideal value).
|
||||
*
|
||||
* Comments below will note which reference frequency is assumed for various
|
||||
* parameters. They will usually be one of
|
||||
*
|
||||
* ref_freq = 28.636360 MHz
|
||||
* or
|
||||
* ref_freq = 28.636363 MHz
|
||||
*/
|
||||
|
||||
static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq)
|
||||
{
|
||||
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
||||
|
||||
if (state->aud_input != CX25840_AUDIO_SERIAL) {
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
/*
|
||||
* VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
|
||||
* AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10
|
||||
*/
|
||||
cx25840_write4(client, 0x108, 0x1006040f);
|
||||
|
||||
/*
|
||||
* VID_PLL Fraction (register 0x10c) = 0x2be2fe
|
||||
* 28636360 * 0xf.15f17f0/4 = 108 MHz
|
||||
* 432 MHz pre-postdivide
|
||||
*/
|
||||
|
||||
/*
|
||||
* AUX_PLL Fraction = 0x1bb39ee
|
||||
* 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384
|
||||
* 196.6 MHz pre-postdivide
|
||||
* FIXME < 200 MHz is out of specified valid range
|
||||
* FIXME 28636363 ref_freq doesn't match VID PLL ref
|
||||
*/
|
||||
cx25840_write4(client, 0x110, 0x01bb39ee);
|
||||
|
||||
/*
|
||||
* SA_MCLK_SEL = 1
|
||||
* SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
|
||||
*/
|
||||
cx25840_write(client, 0x127, 0x50);
|
||||
|
||||
if (is_cx2583x(state))
|
||||
break;
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
|
||||
cx25840_write4(client, 0x900, 0x0801f77f);
|
||||
cx25840_write4(client, 0x904, 0x0801f77f);
|
||||
cx25840_write4(client, 0x90c, 0x0801f77f);
|
||||
break;
|
||||
|
||||
case 44100:
|
||||
/*
|
||||
* VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
|
||||
* AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10
|
||||
*/
|
||||
cx25840_write4(client, 0x108, 0x1009040f);
|
||||
|
||||
/*
|
||||
* VID_PLL Fraction (register 0x10c) = 0x2be2fe
|
||||
* 28636360 * 0xf.15f17f0/4 = 108 MHz
|
||||
* 432 MHz pre-postdivide
|
||||
*/
|
||||
|
||||
/*
|
||||
* AUX_PLL Fraction = 0x0ec6bd6
|
||||
* 28636363 * 0x9.7635eb0/0x10 = 44100 * 384
|
||||
* 271 MHz pre-postdivide
|
||||
* FIXME 28636363 ref_freq doesn't match VID PLL ref
|
||||
*/
|
||||
cx25840_write4(client, 0x110, 0x00ec6bd6);
|
||||
|
||||
/*
|
||||
* SA_MCLK_SEL = 1
|
||||
* SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
|
||||
*/
|
||||
cx25840_write(client, 0x127, 0x50);
|
||||
|
||||
if (is_cx2583x(state))
|
||||
break;
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
|
||||
cx25840_write4(client, 0x900, 0x08016d59);
|
||||
cx25840_write4(client, 0x904, 0x08016d59);
|
||||
cx25840_write4(client, 0x90c, 0x08016d59);
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
/*
|
||||
* VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
|
||||
* AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10
|
||||
*/
|
||||
cx25840_write4(client, 0x108, 0x100a040f);
|
||||
|
||||
/*
|
||||
* VID_PLL Fraction (register 0x10c) = 0x2be2fe
|
||||
* 28636360 * 0xf.15f17f0/4 = 108 MHz
|
||||
* 432 MHz pre-postdivide
|
||||
*/
|
||||
|
||||
/*
|
||||
* AUX_PLL Fraction = 0x098d6e5
|
||||
* 28636363 * 0xa.4c6b728/0x10 = 48000 * 384
|
||||
* 295 MHz pre-postdivide
|
||||
* FIXME 28636363 ref_freq doesn't match VID PLL ref
|
||||
*/
|
||||
cx25840_write4(client, 0x110, 0x0098d6e5);
|
||||
|
||||
/*
|
||||
* SA_MCLK_SEL = 1
|
||||
* SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
|
||||
*/
|
||||
cx25840_write(client, 0x127, 0x50);
|
||||
|
||||
if (is_cx2583x(state))
|
||||
break;
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
|
||||
cx25840_write4(client, 0x900, 0x08014faa);
|
||||
cx25840_write4(client, 0x904, 0x08014faa);
|
||||
cx25840_write4(client, 0x90c, 0x08014faa);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
/*
|
||||
* VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
|
||||
* AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e
|
||||
*/
|
||||
cx25840_write4(client, 0x108, 0x1e08040f);
|
||||
|
||||
/*
|
||||
* VID_PLL Fraction (register 0x10c) = 0x2be2fe
|
||||
* 28636360 * 0xf.15f17f0/4 = 108 MHz
|
||||
* 432 MHz pre-postdivide
|
||||
*/
|
||||
|
||||
/*
|
||||
* AUX_PLL Fraction = 0x12a0869
|
||||
* 28636363 * 0x8.9504348/0x1e = 32000 * 256
|
||||
* 246 MHz pre-postdivide
|
||||
* FIXME 28636363 ref_freq doesn't match VID PLL ref
|
||||
*/
|
||||
cx25840_write4(client, 0x110, 0x012a0869);
|
||||
|
||||
/*
|
||||
* SA_MCLK_SEL = 1
|
||||
* SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider
|
||||
*/
|
||||
cx25840_write(client, 0x127, 0x54);
|
||||
|
||||
if (is_cx2583x(state))
|
||||
break;
|
||||
|
||||
/* src1_ctl */
|
||||
/* 0x1.0000 = 32000/32000 */
|
||||
cx25840_write4(client, 0x8f8, 0x08010000);
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x2.0000 = 2 * (32000/32000) */
|
||||
cx25840_write4(client, 0x900, 0x08020000);
|
||||
cx25840_write4(client, 0x904, 0x08020000);
|
||||
cx25840_write4(client, 0x90c, 0x08020000);
|
||||
break;
|
||||
|
||||
case 44100:
|
||||
/*
|
||||
* VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
|
||||
* AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18
|
||||
*/
|
||||
cx25840_write4(client, 0x108, 0x1809040f);
|
||||
|
||||
/*
|
||||
* VID_PLL Fraction (register 0x10c) = 0x2be2fe
|
||||
* 28636360 * 0xf.15f17f0/4 = 108 MHz
|
||||
* 432 MHz pre-postdivide
|
||||
*/
|
||||
|
||||
/*
|
||||
* AUX_PLL Fraction = 0x0ec6bd6
|
||||
* 28636363 * 0x9.7635eb0/0x18 = 44100 * 256
|
||||
* 271 MHz pre-postdivide
|
||||
* FIXME 28636363 ref_freq doesn't match VID PLL ref
|
||||
*/
|
||||
cx25840_write4(client, 0x110, 0x00ec6bd6);
|
||||
|
||||
/*
|
||||
* SA_MCLK_SEL = 1
|
||||
* SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
|
||||
*/
|
||||
cx25840_write(client, 0x127, 0x50);
|
||||
|
||||
if (is_cx2583x(state))
|
||||
break;
|
||||
|
||||
/* src1_ctl */
|
||||
/* 0x1.60cd = 44100/32000 */
|
||||
cx25840_write4(client, 0x8f8, 0x080160cd);
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.7385 = 2 * (32000/44100) */
|
||||
cx25840_write4(client, 0x900, 0x08017385);
|
||||
cx25840_write4(client, 0x904, 0x08017385);
|
||||
cx25840_write4(client, 0x90c, 0x08017385);
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
/*
|
||||
* VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
|
||||
* AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18
|
||||
*/
|
||||
cx25840_write4(client, 0x108, 0x180a040f);
|
||||
|
||||
/*
|
||||
* VID_PLL Fraction (register 0x10c) = 0x2be2fe
|
||||
* 28636360 * 0xf.15f17f0/4 = 108 MHz
|
||||
* 432 MHz pre-postdivide
|
||||
*/
|
||||
|
||||
/*
|
||||
* AUX_PLL Fraction = 0x098d6e5
|
||||
* 28636363 * 0xa.4c6b728/0x18 = 48000 * 256
|
||||
* 295 MHz pre-postdivide
|
||||
* FIXME 28636363 ref_freq doesn't match VID PLL ref
|
||||
*/
|
||||
cx25840_write4(client, 0x110, 0x0098d6e5);
|
||||
|
||||
/*
|
||||
* SA_MCLK_SEL = 1
|
||||
* SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
|
||||
*/
|
||||
cx25840_write(client, 0x127, 0x50);
|
||||
|
||||
if (is_cx2583x(state))
|
||||
break;
|
||||
|
||||
/* src1_ctl */
|
||||
/* 0x1.8000 = 48000/32000 */
|
||||
cx25840_write4(client, 0x8f8, 0x08018000);
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.5555 = 2 * (32000/48000) */
|
||||
cx25840_write4(client, 0x900, 0x08015555);
|
||||
cx25840_write4(client, 0x904, 0x08015555);
|
||||
cx25840_write4(client, 0x90c, 0x08015555);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state->audclk_freq = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq)
|
||||
{
|
||||
return cx25840_set_audclk_freq(client, freq);
|
||||
}
|
||||
|
||||
static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq)
|
||||
{
|
||||
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
||||
|
||||
if (state->aud_input != CX25840_AUDIO_SERIAL) {
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
/* We don't have register values
|
||||
* so avoid destroying registers. */
|
||||
/* FIXME return -EINVAL; */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
case 44100:
|
||||
/* We don't have register values
|
||||
* so avoid destroying registers. */
|
||||
/* FIXME return -EINVAL; */
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
/* src1_ctl */
|
||||
/* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
|
||||
cx25840_write4(client, 0x8f8, 0x0801867c);
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
|
||||
cx25840_write4(client, 0x900, 0x08014faa);
|
||||
cx25840_write4(client, 0x904, 0x08014faa);
|
||||
cx25840_write4(client, 0x90c, 0x08014faa);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state->audclk_freq = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq)
|
||||
{
|
||||
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
||||
|
||||
if (state->aud_input != CX25840_AUDIO_SERIAL) {
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
|
||||
cx25840_write4(client, 0x900, 0x0801f77f);
|
||||
cx25840_write4(client, 0x904, 0x0801f77f);
|
||||
cx25840_write4(client, 0x90c, 0x0801f77f);
|
||||
break;
|
||||
|
||||
case 44100:
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
|
||||
cx25840_write4(client, 0x900, 0x08016d59);
|
||||
cx25840_write4(client, 0x904, 0x08016d59);
|
||||
cx25840_write4(client, 0x90c, 0x08016d59);
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
|
||||
cx25840_write4(client, 0x900, 0x08014faa);
|
||||
cx25840_write4(client, 0x904, 0x08014faa);
|
||||
cx25840_write4(client, 0x90c, 0x08014faa);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (freq) {
|
||||
/* FIXME These cases make different assumptions about audclk */
|
||||
case 32000:
|
||||
/* src1_ctl */
|
||||
/* 0x1.0000 = 32000/32000 */
|
||||
cx25840_write4(client, 0x8f8, 0x08010000);
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x2.0000 = 2 * (32000/32000) */
|
||||
cx25840_write4(client, 0x900, 0x08020000);
|
||||
cx25840_write4(client, 0x904, 0x08020000);
|
||||
cx25840_write4(client, 0x90c, 0x08020000);
|
||||
break;
|
||||
|
||||
case 44100:
|
||||
/* src1_ctl */
|
||||
/* 0x1.60cd = 44100/32000 */
|
||||
cx25840_write4(client, 0x8f8, 0x080160cd);
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.7385 = 2 * (32000/44100) */
|
||||
cx25840_write4(client, 0x900, 0x08017385);
|
||||
cx25840_write4(client, 0x904, 0x08017385);
|
||||
cx25840_write4(client, 0x90c, 0x08017385);
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
/* src1_ctl */
|
||||
/* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
|
||||
cx25840_write4(client, 0x8f8, 0x0801867c);
|
||||
|
||||
/* src3/4/6_ctl */
|
||||
/* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
|
||||
cx25840_write4(client, 0x900, 0x08014faa);
|
||||
cx25840_write4(client, 0x904, 0x08014faa);
|
||||
cx25840_write4(client, 0x90c, 0x08014faa);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state->audclk_freq = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
||||
{
|
||||
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
||||
|
||||
if (freq != 32000 && freq != 44100 && freq != 48000)
|
||||
return -EINVAL;
|
||||
|
||||
if (is_cx231xx(state))
|
||||
return cx231xx_set_audclk_freq(client, freq);
|
||||
|
||||
if (is_cx2388x(state))
|
||||
return cx23885_set_audclk_freq(client, freq);
|
||||
|
||||
if (is_cx2583x(state))
|
||||
return cx25836_set_audclk_freq(client, freq);
|
||||
|
||||
return cx25840_set_audclk_freq(client, freq);
|
||||
}
|
||||
|
||||
void cx25840_audio_set_path(struct i2c_client *client)
|
||||
{
|
||||
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
||||
|
||||
if (!is_cx2583x(state)) {
|
||||
/* assert soft reset */
|
||||
cx25840_and_or(client, 0x810, ~0x1, 0x01);
|
||||
|
||||
/* stop microcontroller */
|
||||
cx25840_and_or(client, 0x803, ~0x10, 0);
|
||||
|
||||
/* Mute everything to prevent the PFFT! */
|
||||
cx25840_write(client, 0x8d3, 0x1f);
|
||||
|
||||
if (state->aud_input == CX25840_AUDIO_SERIAL) {
|
||||
/* Set Path1 to Serial Audio Input */
|
||||
cx25840_write4(client, 0x8d0, 0x01011012);
|
||||
|
||||
/* The microcontroller should not be started for the
|
||||
* non-tuner inputs: autodetection is specific for
|
||||
* TV audio. */
|
||||
} else {
|
||||
/* Set Path1 to Analog Demod Main Channel */
|
||||
cx25840_write4(client, 0x8d0, 0x1f063870);
|
||||
}
|
||||
}
|
||||
|
||||
set_audclk_freq(client, state->audclk_freq);
|
||||
|
||||
if (!is_cx2583x(state)) {
|
||||
if (state->aud_input != CX25840_AUDIO_SERIAL) {
|
||||
/* When the microcontroller detects the
|
||||
* audio format, it will unmute the lines */
|
||||
cx25840_and_or(client, 0x803, ~0x10, 0x10);
|
||||
}
|
||||
|
||||
/* deassert soft reset */
|
||||
cx25840_and_or(client, 0x810, ~0x1, 0x00);
|
||||
|
||||
/* Ensure the controller is running when we exit */
|
||||
if (is_cx2388x(state) || is_cx231xx(state))
|
||||
cx25840_and_or(client, 0x803, ~0x10, 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_volume(struct i2c_client *client, int volume)
|
||||
{
|
||||
int vol;
|
||||
|
||||
/* Convert the volume to msp3400 values (0-127) */
|
||||
vol = volume >> 9;
|
||||
|
||||
/* now scale it up to cx25840 values
|
||||
* -114dB to -96dB maps to 0
|
||||
* this should be 19, but in my testing that was 4dB too loud */
|
||||
if (vol <= 23) {
|
||||
vol = 0;
|
||||
} else {
|
||||
vol -= 23;
|
||||
}
|
||||
|
||||
/* PATH1_VOLUME */
|
||||
cx25840_write(client, 0x8d4, 228 - (vol * 2));
|
||||
}
|
||||
|
||||
static void set_balance(struct i2c_client *client, int balance)
|
||||
{
|
||||
int bal = balance >> 8;
|
||||
if (bal > 0x80) {
|
||||
/* PATH1_BAL_LEFT */
|
||||
cx25840_and_or(client, 0x8d5, 0x7f, 0x80);
|
||||
/* PATH1_BAL_LEVEL */
|
||||
cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f);
|
||||
} else {
|
||||
/* PATH1_BAL_LEFT */
|
||||
cx25840_and_or(client, 0x8d5, 0x7f, 0x00);
|
||||
/* PATH1_BAL_LEVEL */
|
||||
cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal);
|
||||
}
|
||||
}
|
||||
|
||||
int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct cx25840_state *state = to_state(sd);
|
||||
int retval;
|
||||
|
||||
if (!is_cx2583x(state))
|
||||
cx25840_and_or(client, 0x810, ~0x1, 1);
|
||||
if (state->aud_input != CX25840_AUDIO_SERIAL) {
|
||||
cx25840_and_or(client, 0x803, ~0x10, 0);
|
||||
cx25840_write(client, 0x8d3, 0x1f);
|
||||
}
|
||||
retval = set_audclk_freq(client, freq);
|
||||
if (state->aud_input != CX25840_AUDIO_SERIAL)
|
||||
cx25840_and_or(client, 0x803, ~0x10, 0x10);
|
||||
if (!is_cx2583x(state))
|
||||
cx25840_and_or(client, 0x810, ~0x1, 0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct v4l2_subdev *sd = to_sd(ctrl);
|
||||
struct cx25840_state *state = to_state(sd);
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
if (state->mute->val)
|
||||
set_volume(client, 0);
|
||||
else
|
||||
set_volume(client, state->volume->val);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
/* PATH1_EQ_BASS_VOL */
|
||||
cx25840_and_or(client, 0x8d9, ~0x3f,
|
||||
48 - (ctrl->val * 48 / 0xffff));
|
||||
break;
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
/* PATH1_EQ_TREBLE_VOL */
|
||||
cx25840_and_or(client, 0x8db, ~0x3f,
|
||||
48 - (ctrl->val * 48 / 0xffff));
|
||||
break;
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
set_balance(client, ctrl->val);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = {
|
||||
.s_ctrl = cx25840_audio_s_ctrl,
|
||||
};
|
||||
5320
drivers/media/i2c/cx25840/cx25840-core.c
Normal file
5320
drivers/media/i2c/cx25840/cx25840-core.c
Normal file
File diff suppressed because it is too large
Load diff
149
drivers/media/i2c/cx25840/cx25840-core.h
Normal file
149
drivers/media/i2c/cx25840/cx25840-core.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/* cx25840 internal API header
|
||||
*
|
||||
* Copyright (C) 2003-2004 Chris Kennedy
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _CX25840_CORE_H_
|
||||
#define _CX25840_CORE_H_
|
||||
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
struct cx25840_ir_state;
|
||||
|
||||
enum cx25840_model {
|
||||
CX23885_AV,
|
||||
CX23887_AV,
|
||||
CX23888_AV,
|
||||
CX2310X_AV,
|
||||
CX25840,
|
||||
CX25841,
|
||||
CX25842,
|
||||
CX25843,
|
||||
CX25836,
|
||||
CX25837,
|
||||
};
|
||||
|
||||
struct cx25840_state {
|
||||
struct i2c_client *c;
|
||||
struct v4l2_subdev sd;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct {
|
||||
/* volume cluster */
|
||||
struct v4l2_ctrl *volume;
|
||||
struct v4l2_ctrl *mute;
|
||||
};
|
||||
int pvr150_workaround;
|
||||
int radio;
|
||||
v4l2_std_id std;
|
||||
enum cx25840_video_input vid_input;
|
||||
enum cx25840_audio_input aud_input;
|
||||
u32 audclk_freq;
|
||||
int audmode;
|
||||
int vbi_line_offset;
|
||||
enum cx25840_model id;
|
||||
u32 rev;
|
||||
int is_initialized;
|
||||
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
|
||||
struct work_struct fw_work; /* work entry for fw load */
|
||||
struct cx25840_ir_state *ir_state;
|
||||
};
|
||||
|
||||
static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
|
||||
{
|
||||
return container_of(sd, struct cx25840_state, sd);
|
||||
}
|
||||
|
||||
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd;
|
||||
}
|
||||
|
||||
static inline bool is_cx2583x(struct cx25840_state *state)
|
||||
{
|
||||
return state->id == CX25836 ||
|
||||
state->id == CX25837;
|
||||
}
|
||||
|
||||
static inline bool is_cx231xx(struct cx25840_state *state)
|
||||
{
|
||||
return state->id == CX2310X_AV;
|
||||
}
|
||||
|
||||
static inline bool is_cx2388x(struct cx25840_state *state)
|
||||
{
|
||||
return state->id == CX23885_AV ||
|
||||
state->id == CX23887_AV ||
|
||||
state->id == CX23888_AV;
|
||||
}
|
||||
|
||||
static inline bool is_cx23885(struct cx25840_state *state)
|
||||
{
|
||||
return state->id == CX23885_AV;
|
||||
}
|
||||
|
||||
static inline bool is_cx23887(struct cx25840_state *state)
|
||||
{
|
||||
return state->id == CX23887_AV;
|
||||
}
|
||||
|
||||
static inline bool is_cx23888(struct cx25840_state *state)
|
||||
{
|
||||
return state->id == CX23888_AV;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx25850-core.c */
|
||||
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
|
||||
int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
|
||||
u8 cx25840_read(struct i2c_client *client, u16 addr);
|
||||
u32 cx25840_read4(struct i2c_client *client, u16 addr);
|
||||
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
|
||||
int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
|
||||
u32 or_value);
|
||||
void cx25840_std_setup(struct i2c_client *client);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx25850-firmware.c */
|
||||
int cx25840_loadfw(struct i2c_client *client);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx25850-audio.c */
|
||||
void cx25840_audio_set_path(struct i2c_client *client);
|
||||
int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
|
||||
|
||||
extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops;
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx25850-vbi.c */
|
||||
int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt);
|
||||
int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
|
||||
int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
|
||||
int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx25850-ir.c */
|
||||
extern const struct v4l2_subdev_ir_ops cx25840_ir_ops;
|
||||
int cx25840_ir_log_status(struct v4l2_subdev *sd);
|
||||
int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled);
|
||||
int cx25840_ir_probe(struct v4l2_subdev *sd);
|
||||
int cx25840_ir_remove(struct v4l2_subdev *sd);
|
||||
|
||||
#endif
|
||||
175
drivers/media/i2c/cx25840/cx25840-firmware.c
Normal file
175
drivers/media/i2c/cx25840/cx25840-firmware.c
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/* cx25840 firmware functions
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/cx25840.h>
|
||||
|
||||
#include "cx25840-core.h"
|
||||
|
||||
/*
|
||||
* Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
|
||||
* size of the firmware chunks sent down the I2C bus to the chip.
|
||||
* Previously this had been set to 1024 but unfortunately some I2C
|
||||
* implementations can't transfer data in such big gulps.
|
||||
* Specifically, the pvrusb2 driver has a hard limit of around 60
|
||||
* bytes, due to the encapsulation there of I2C traffic into USB
|
||||
* messages. So we have to significantly reduce this parameter.
|
||||
*/
|
||||
#define FWSEND 48
|
||||
|
||||
#define FWDEV(x) &((x)->dev)
|
||||
|
||||
static char *firmware = "";
|
||||
|
||||
module_param(firmware, charp, 0444);
|
||||
|
||||
MODULE_PARM_DESC(firmware, "Firmware image to load");
|
||||
|
||||
static void start_fw_load(struct i2c_client *client)
|
||||
{
|
||||
/* DL_ADDR_LB=0 DL_ADDR_HB=0 */
|
||||
cx25840_write(client, 0x800, 0x00);
|
||||
cx25840_write(client, 0x801, 0x00);
|
||||
// DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1
|
||||
cx25840_write(client, 0x803, 0x0b);
|
||||
/* AUTO_INC_DIS=1 */
|
||||
cx25840_write(client, 0x000, 0x20);
|
||||
}
|
||||
|
||||
static void end_fw_load(struct i2c_client *client)
|
||||
{
|
||||
/* AUTO_INC_DIS=0 */
|
||||
cx25840_write(client, 0x000, 0x00);
|
||||
/* DL_ENABLE=0 */
|
||||
cx25840_write(client, 0x803, 0x03);
|
||||
}
|
||||
|
||||
#define CX2388x_FIRMWARE "v4l-cx23885-avcore-01.fw"
|
||||
#define CX231xx_FIRMWARE "v4l-cx231xx-avcore-01.fw"
|
||||
#define CX25840_FIRMWARE "v4l-cx25840.fw"
|
||||
|
||||
static const char *get_fw_name(struct i2c_client *client)
|
||||
{
|
||||
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
||||
|
||||
if (firmware[0])
|
||||
return firmware;
|
||||
if (is_cx2388x(state))
|
||||
return CX2388x_FIRMWARE;
|
||||
if (is_cx231xx(state))
|
||||
return CX231xx_FIRMWARE;
|
||||
return CX25840_FIRMWARE;
|
||||
}
|
||||
|
||||
static int check_fw_load(struct i2c_client *client, int size)
|
||||
{
|
||||
/* DL_ADDR_HB DL_ADDR_LB */
|
||||
int s = cx25840_read(client, 0x801) << 8;
|
||||
s |= cx25840_read(client, 0x800);
|
||||
|
||||
if (size != s) {
|
||||
v4l_err(client, "firmware %s load failed\n",
|
||||
get_fw_name(client));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
v4l_info(client, "loaded %s firmware (%d bytes)\n",
|
||||
get_fw_name(client), size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fw_write(struct i2c_client *client, const u8 *data, int size)
|
||||
{
|
||||
if (i2c_master_send(client, data, size) < size) {
|
||||
v4l_err(client, "firmware load i2c failure\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx25840_loadfw(struct i2c_client *client)
|
||||
{
|
||||
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
||||
const struct firmware *fw = NULL;
|
||||
u8 buffer[FWSEND];
|
||||
const u8 *ptr;
|
||||
const char *fwname = get_fw_name(client);
|
||||
int size, retval;
|
||||
int MAX_BUF_SIZE = FWSEND;
|
||||
u32 gpio_oe = 0, gpio_da = 0;
|
||||
|
||||
if (is_cx2388x(state)) {
|
||||
/* Preserve the GPIO OE and output bits */
|
||||
gpio_oe = cx25840_read(client, 0x160);
|
||||
gpio_da = cx25840_read(client, 0x164);
|
||||
}
|
||||
|
||||
if (is_cx231xx(state) && MAX_BUF_SIZE > 16) {
|
||||
v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
|
||||
MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
|
||||
}
|
||||
|
||||
if (request_firmware(&fw, fwname, FWDEV(client)) != 0) {
|
||||
v4l_err(client, "unable to open firmware %s\n", fwname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
start_fw_load(client);
|
||||
|
||||
buffer[0] = 0x08;
|
||||
buffer[1] = 0x02;
|
||||
|
||||
size = fw->size;
|
||||
ptr = fw->data;
|
||||
while (size > 0) {
|
||||
int len = min(MAX_BUF_SIZE - 2, size);
|
||||
|
||||
memcpy(buffer + 2, ptr, len);
|
||||
|
||||
retval = fw_write(client, buffer, len + 2);
|
||||
|
||||
if (retval < 0) {
|
||||
release_firmware(fw);
|
||||
return retval;
|
||||
}
|
||||
|
||||
size -= len;
|
||||
ptr += len;
|
||||
}
|
||||
|
||||
end_fw_load(client);
|
||||
|
||||
size = fw->size;
|
||||
release_firmware(fw);
|
||||
|
||||
if (is_cx2388x(state)) {
|
||||
/* Restore GPIO configuration after f/w load */
|
||||
cx25840_write(client, 0x160, gpio_oe);
|
||||
cx25840_write(client, 0x164, gpio_da);
|
||||
}
|
||||
|
||||
return check_fw_load(client, size);
|
||||
}
|
||||
|
||||
MODULE_FIRMWARE(CX2388x_FIRMWARE);
|
||||
MODULE_FIRMWARE(CX231xx_FIRMWARE);
|
||||
MODULE_FIRMWARE(CX25840_FIRMWARE);
|
||||
|
||||
1276
drivers/media/i2c/cx25840/cx25840-ir.c
Normal file
1276
drivers/media/i2c/cx25840/cx25840-ir.c
Normal file
File diff suppressed because it is too large
Load diff
257
drivers/media/i2c/cx25840/cx25840-vbi.c
Normal file
257
drivers/media/i2c/cx25840/cx25840-vbi.c
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
/* cx25840 VBI functions
|
||||
*
|
||||
* 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/videodev2.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/cx25840.h>
|
||||
|
||||
#include "cx25840-core.h"
|
||||
|
||||
static int odd_parity(u8 c)
|
||||
{
|
||||
c ^= (c >> 4);
|
||||
c ^= (c >> 2);
|
||||
c ^= (c >> 1);
|
||||
|
||||
return c & 1;
|
||||
}
|
||||
|
||||
static int decode_vps(u8 * dst, u8 * p)
|
||||
{
|
||||
static const u8 biphase_tbl[] = {
|
||||
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
|
||||
0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
|
||||
0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
|
||||
0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
|
||||
0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
|
||||
0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
|
||||
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
|
||||
0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
|
||||
0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
|
||||
0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
|
||||
0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
|
||||
0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
|
||||
0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
|
||||
0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
|
||||
0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
|
||||
0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
|
||||
0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
|
||||
0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
|
||||
0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
|
||||
0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
|
||||
0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
|
||||
0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
|
||||
0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
|
||||
0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
|
||||
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
|
||||
0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
|
||||
0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
|
||||
0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
|
||||
0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
|
||||
0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
|
||||
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
|
||||
0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
|
||||
};
|
||||
|
||||
u8 c, err = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2 * 13; i += 2) {
|
||||
err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
|
||||
c = (biphase_tbl[p[i + 1]] & 0xf) |
|
||||
((biphase_tbl[p[i]] & 0xf) << 4);
|
||||
dst[i / 2] = c;
|
||||
}
|
||||
|
||||
return err & 0xf0;
|
||||
}
|
||||
|
||||
int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct cx25840_state *state = to_state(sd);
|
||||
static const u16 lcr2vbi[] = {
|
||||
0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
|
||||
0, V4L2_SLICED_WSS_625, 0, /* 4 */
|
||||
V4L2_SLICED_CAPTION_525, /* 6 */
|
||||
0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
|
||||
0, 0, 0, 0
|
||||
};
|
||||
int is_pal = !(state->std & V4L2_STD_525_60);
|
||||
int i;
|
||||
|
||||
memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
|
||||
svbi->service_set = 0;
|
||||
/* we're done if raw VBI is active */
|
||||
if ((cx25840_read(client, 0x404) & 0x10) == 0)
|
||||
return 0;
|
||||
|
||||
if (is_pal) {
|
||||
for (i = 7; i <= 23; i++) {
|
||||
u8 v = cx25840_read(client, 0x424 + i - 7);
|
||||
|
||||
svbi->service_lines[0][i] = lcr2vbi[v >> 4];
|
||||
svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
|
||||
svbi->service_set |= svbi->service_lines[0][i] |
|
||||
svbi->service_lines[1][i];
|
||||
}
|
||||
} else {
|
||||
for (i = 10; i <= 21; i++) {
|
||||
u8 v = cx25840_read(client, 0x424 + i - 10);
|
||||
|
||||
svbi->service_lines[0][i] = lcr2vbi[v >> 4];
|
||||
svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
|
||||
svbi->service_set |= svbi->service_lines[0][i] |
|
||||
svbi->service_lines[1][i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct cx25840_state *state = to_state(sd);
|
||||
int is_pal = !(state->std & V4L2_STD_525_60);
|
||||
int vbi_offset = is_pal ? 1 : 0;
|
||||
|
||||
/* Setup standard */
|
||||
cx25840_std_setup(client);
|
||||
|
||||
/* VBI Offset */
|
||||
cx25840_write(client, 0x47f, vbi_offset);
|
||||
cx25840_write(client, 0x404, 0x2e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct cx25840_state *state = to_state(sd);
|
||||
int is_pal = !(state->std & V4L2_STD_525_60);
|
||||
int vbi_offset = is_pal ? 1 : 0;
|
||||
int i, x;
|
||||
u8 lcr[24];
|
||||
|
||||
for (x = 0; x <= 23; x++)
|
||||
lcr[x] = 0x00;
|
||||
|
||||
/* Setup standard */
|
||||
cx25840_std_setup(client);
|
||||
|
||||
/* Sliced VBI */
|
||||
cx25840_write(client, 0x404, 0x32); /* Ancillary data */
|
||||
cx25840_write(client, 0x406, 0x13);
|
||||
cx25840_write(client, 0x47f, vbi_offset);
|
||||
|
||||
if (is_pal) {
|
||||
for (i = 0; i <= 6; i++)
|
||||
svbi->service_lines[0][i] =
|
||||
svbi->service_lines[1][i] = 0;
|
||||
} else {
|
||||
for (i = 0; i <= 9; i++)
|
||||
svbi->service_lines[0][i] =
|
||||
svbi->service_lines[1][i] = 0;
|
||||
|
||||
for (i = 22; i <= 23; i++)
|
||||
svbi->service_lines[0][i] =
|
||||
svbi->service_lines[1][i] = 0;
|
||||
}
|
||||
|
||||
for (i = 7; i <= 23; i++) {
|
||||
for (x = 0; x <= 1; x++) {
|
||||
switch (svbi->service_lines[1-x][i]) {
|
||||
case V4L2_SLICED_TELETEXT_B:
|
||||
lcr[i] |= 1 << (4 * x);
|
||||
break;
|
||||
case V4L2_SLICED_WSS_625:
|
||||
lcr[i] |= 4 << (4 * x);
|
||||
break;
|
||||
case V4L2_SLICED_CAPTION_525:
|
||||
lcr[i] |= 6 << (4 * x);
|
||||
break;
|
||||
case V4L2_SLICED_VPS:
|
||||
lcr[i] |= 9 << (4 * x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_pal) {
|
||||
for (x = 1, i = 0x424; i <= 0x434; i++, x++)
|
||||
cx25840_write(client, i, lcr[6 + x]);
|
||||
} else {
|
||||
for (x = 1, i = 0x424; i <= 0x430; i++, x++)
|
||||
cx25840_write(client, i, lcr[9 + x]);
|
||||
for (i = 0x431; i <= 0x434; i++)
|
||||
cx25840_write(client, i, 0);
|
||||
}
|
||||
|
||||
cx25840_write(client, 0x43c, 0x16);
|
||||
cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
|
||||
{
|
||||
struct cx25840_state *state = to_state(sd);
|
||||
u8 *p = vbi->p;
|
||||
int id1, id2, l, err = 0;
|
||||
|
||||
if (p[0] || p[1] != 0xff || p[2] != 0xff ||
|
||||
(p[3] != 0x55 && p[3] != 0x91)) {
|
||||
vbi->line = vbi->type = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += 4;
|
||||
id1 = p[-1];
|
||||
id2 = p[0] & 0xf;
|
||||
l = p[2] & 0x3f;
|
||||
l += state->vbi_line_offset;
|
||||
p += 4;
|
||||
|
||||
switch (id2) {
|
||||
case 1:
|
||||
id2 = V4L2_SLICED_TELETEXT_B;
|
||||
break;
|
||||
case 4:
|
||||
id2 = V4L2_SLICED_WSS_625;
|
||||
break;
|
||||
case 6:
|
||||
id2 = V4L2_SLICED_CAPTION_525;
|
||||
err = !odd_parity(p[0]) || !odd_parity(p[1]);
|
||||
break;
|
||||
case 9:
|
||||
id2 = V4L2_SLICED_VPS;
|
||||
if (decode_vps(p, p) != 0)
|
||||
err = 1;
|
||||
break;
|
||||
default:
|
||||
id2 = 0;
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
vbi->type = err ? 0 : id2;
|
||||
vbi->line = err ? 0 : l;
|
||||
vbi->is_second_field = err ? 0 : (id1 == 0x55);
|
||||
vbi->p = p;
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue