Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View 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

View 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

View 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,
};

File diff suppressed because it is too large Load diff

View 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

View 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);

File diff suppressed because it is too large Load diff

View 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;
}