Skip to content

Commit

Permalink
V4L/DVB (9512): cx18: Fix write retries for registers that always cha…
Browse files Browse the repository at this point in the history
…nge - part 3.

cx18: Fix write retries for registers that always change - part 3.
Fix the io for the rest of the registers that will often not read back the
value just written.  Modified register readback checks to make sure the
intended effect was achieved without constantly rewriting the registers.
The one outstanding register remaining is 0xc72014 CX18_AUDIO_ENABLE, whose
behavior on writes I have yet to determine.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Andy Walls authored and Mauro Carvalho Chehab committed Dec 29, 2008
1 parent 48fc6bb commit ced0737
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 80 deletions.
41 changes: 30 additions & 11 deletions drivers/media/video/cx18/cx18-av-audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,15 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
void cx18_av_audio_set_path(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
u8 v;

/* stop microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0);
v = cx18_av_read(cx, 0x803) & ~0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);

/* assert soft reset */
cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
v = cx18_av_read(cx, 0x810) | 0x01;
cx18_av_write_expect(cx, 0x810, v, v, 0x0f);

/* Mute everything to prevent the PFFT! */
cx18_av_write(cx, 0x8d3, 0x1f);
Expand All @@ -240,12 +243,14 @@ void cx18_av_audio_set_path(struct cx18 *cx)
set_audclk_freq(cx, state->audclk_freq);

/* deassert soft reset */
cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
v = cx18_av_read(cx, 0x810) & ~0x01;
cx18_av_write_expect(cx, 0x810, v, v, 0x0f);

if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* When the microcontroller detects the
* audio format, it will unmute the lines */
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
v = cx18_av_read(cx, 0x803) | 0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
}

Expand Down Expand Up @@ -347,19 +352,23 @@ static int get_mute(struct cx18 *cx)
static void set_mute(struct cx18 *cx, int mute)
{
struct cx18_av_state *state = &cx->av_state;
u8 v;

if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* Must turn off microcontroller in order to mute sound.
* Not sure if this is the best method, but it does work.
* If the microcontroller is running, then it will undo any
* changes to the mute register. */
v = cx18_av_read(cx, 0x803);
if (mute) {
/* disable microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
v &= ~0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
cx18_av_write(cx, 0x8d3, 0x1f);
} else {
/* enable microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
v |= 0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
} else {
/* SRC1_MUTE_EN */
Expand All @@ -375,16 +384,26 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)

switch (cmd) {
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
{
u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
cx18_av_and_or(cx, 0x803, ~0x10, 0);
v = cx18_av_read(cx, 0x803) & ~0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
cx18_av_write(cx, 0x8d3, 0x1f);
}
cx18_av_and_or(cx, 0x810, ~0x1, 1);
v = cx18_av_read(cx, 0x810) | 0x1;
cx18_av_write_expect(cx, 0x810, v, v, 0x0f);

retval = set_audclk_freq(cx, *(u32 *)arg);
cx18_av_and_or(cx, 0x810, ~0x1, 0);
if (state->aud_input > CX18_AV_AUDIO_SERIAL2)
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);

v = cx18_av_read(cx, 0x810) & ~0x1;
cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
v = cx18_av_read(cx, 0x803) | 0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
return retval;
}

case VIDIOC_G_CTRL:
switch (ctrl->id) {
Expand Down
95 changes: 68 additions & 27 deletions drivers/media/video/cx18/cx18-av-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,31 @@ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
return 0;
}

int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask)
{
u32 reg = 0xc40000 + (addr & ~3);
int shift = (addr & 3) * 8;
u32 x = cx18_read_reg(cx, reg);

x = (x & ~((u32)0xff << shift)) | ((u32)value << shift);
cx18_write_reg_expect(cx, x, reg,
((u32)eval << shift), ((u32)mask << shift));
return 0;
}

int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
{
cx18_write_reg(cx, value, 0xc40000 + addr);
return 0;
}

int
cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask)
{
cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask);
return 0;
}

int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value)
{
cx18_write_reg_noretry(cx, value, 0xc40000 + addr);
Expand Down Expand Up @@ -98,14 +117,16 @@ static void cx18_av_initialize(struct cx18 *cx)

cx18_av_loadfw(cx);
/* Stop 8051 code execution */
cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000);
cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000,
0x03000000, 0x13000000);

/* initallize the PLL by toggling sleep bit */
v = cx18_av_read4(cx, CXADEC_HOST_REG1);
/* enable sleep mode */
cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1);
/* enable sleep mode - register appears to be read only... */
cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe);
/* disable sleep mode */
cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe);
cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe,
v & 0xfffe, 0xffff);

/* initialize DLLs */
v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
Expand All @@ -125,9 +146,10 @@ static void cx18_av_initialize(struct cx18 *cx)

v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
/* enable TUNE_FIL_RST */
cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v);
cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F);
/* disable TUNE_FIL_RST */
cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE);
cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3,
v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F);

/* enable 656 output */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
Expand Down Expand Up @@ -324,6 +346,7 @@ static void input_change(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
v4l2_std_id std = state->std;
u8 v;

/* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
Expand All @@ -333,31 +356,34 @@ static void input_change(struct cx18 *cx)
if (std & V4L2_STD_525_60) {
if (std == V4L2_STD_NTSC_M_JP) {
/* Japan uses EIAJ audio standard */
cx18_av_write(cx, 0x808, 0xf7);
cx18_av_write(cx, 0x80b, 0x02);
cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff);
cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f);
} else if (std == V4L2_STD_NTSC_M_KR) {
/* South Korea uses A2 audio standard */
cx18_av_write(cx, 0x808, 0xf8);
cx18_av_write(cx, 0x80b, 0x03);
cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff);
cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else {
/* Others use the BTSC audio standard */
cx18_av_write(cx, 0x808, 0xf6);
cx18_av_write(cx, 0x80b, 0x01);
cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff);
cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f);
}
} else if (std & V4L2_STD_PAL) {
/* Follow tuner change procedure for PAL */
cx18_av_write(cx, 0x808, 0xff);
cx18_av_write(cx, 0x80b, 0x03);
cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else if (std & V4L2_STD_SECAM) {
/* Select autodetect for SECAM */
cx18_av_write(cx, 0x808, 0xff);
cx18_av_write(cx, 0x80b, 0x03);
cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
}

if (cx18_av_read(cx, 0x803) & 0x10) {
v = cx18_av_read(cx, 0x803);
if (v & 0x10) {
/* restart audio decoder microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
v &= ~0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
v |= 0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
}

Expand All @@ -368,6 +394,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
vid_input <= CX18_AV_COMPOSITE8);
u8 reg;
u8 v;

CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
vid_input, aud_input);
Expand Down Expand Up @@ -413,16 +440,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
return -EINVAL;
}

cx18_av_write(cx, 0x103, reg);
cx18_av_write_expect(cx, 0x103, reg, reg, 0xf7);
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);

/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
v = cx18_av_read(cx, 0x102);
if (reg & 0x80)
v &= ~0x2;
else
v |= 0x2;
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
cx18_av_and_or(cx, 0x102, ~0x4, 4);
v |= 0x4;
else
cx18_av_and_or(cx, 0x102, ~0x4, 0);
v &= ~0x4;
cx18_av_write_expect(cx, 0x102, v, v, 0x17);

/*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/

state->vid_input = vid_input;
Expand Down Expand Up @@ -799,40 +833,47 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
}

case VIDIOC_S_TUNER:
{
u8 v;

if (state->radio)
break;

v = cx18_av_read(cx, 0x809);
v &= ~0xf;

switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
/* mono -> mono
stereo -> mono
bilingual -> lang1 */
cx18_av_and_or(cx, 0x809, ~0xf, 0x00);
break;
case V4L2_TUNER_MODE_STEREO:
case V4L2_TUNER_MODE_LANG1:
/* mono -> mono
stereo -> stereo
bilingual -> lang1 */
cx18_av_and_or(cx, 0x809, ~0xf, 0x04);
v |= 0x4;
break;
case V4L2_TUNER_MODE_LANG1_LANG2:
/* mono -> mono
stereo -> stereo
bilingual -> lang1/lang2 */
cx18_av_and_or(cx, 0x809, ~0xf, 0x07);
v |= 0x7;
break;
case V4L2_TUNER_MODE_LANG2:
/* mono -> mono
stereo -> stereo
bilingual -> lang2 */
cx18_av_and_or(cx, 0x809, ~0xf, 0x01);
v |= 0x1;
break;
default:
return -EINVAL;
}
cx18_av_write_expect(cx, 0x809, v, v, 0xff);
state->audmode = vt->audmode;
break;
}

case VIDIOC_G_FMT:
return get_v4lfmt(cx, (struct v4l2_format *)arg);
Expand Down
3 changes: 3 additions & 0 deletions drivers/media/video/cx18/cx18-av-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ struct cx18_av_state {
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value);
int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask);
int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval,
u32 mask);
u8 cx18_av_read(struct cx18 *cx, u16 addr);
u32 cx18_av_read4(struct cx18 *cx, u16 addr);
u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr);
Expand Down
18 changes: 11 additions & 7 deletions drivers/media/video/cx18/cx18-av-firmware.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ int cx18_av_loadfw(struct cx18 *cx)
/* The firmware load often has byte errors, so allow for several
retries, both at byte level and at the firmware load level. */
while (retries1 < 5) {
cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6);
cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
0x00008430, 0xffffffff); /* cx25843 */
cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);

/* Reset the Mako core (Register is undocumented.) */
cx18_av_write4(cx, 0x8100, 0x00010000);
/* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
cx18_av_write4_expect(cx, 0x8100, 0x00010000,
0x00008430, 0xffffffff); /* cx25843 */

/* Put the 8051 in reset and enable firmware upload */
cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
Expand Down Expand Up @@ -93,7 +95,8 @@ int cx18_av_loadfw(struct cx18 *cx)
return -EIO;
}

cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
cx18_av_write4_expect(cx, CXADEC_DL_CTL,
0x13000000 | fw->size, 0x13000000, 0x13000000);

/* Output to the 416 */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
Expand All @@ -118,7 +121,8 @@ int cx18_av_loadfw(struct cx18 *cx)
passthrough */
cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);

cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6);
cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
0x3F00FFFF);
/* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */

/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
Expand All @@ -136,7 +140,7 @@ int cx18_av_loadfw(struct cx18 *cx)
v |= 0xFF; /* Auto by default */
v |= 0x400; /* Stereo by default */
v |= 0x14000000;
cx18_av_write4(cx, CXADEC_STD_DET_CTL, v);
cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF);

release_firmware(fw);

Expand Down
Loading

0 comments on commit ced0737

Please sign in to comment.