Commit ced07371 authored by Andy Walls's avatar Andy Walls Committed by Mauro Carvalho Chehab

V4L/DVB (9512): cx18: Fix write retries for registers that always change - 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: default avatarAndy Walls <awalls@radix.net>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 48fc6bb3
...@@ -215,12 +215,15 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) ...@@ -215,12 +215,15 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
void cx18_av_audio_set_path(struct cx18 *cx) void cx18_av_audio_set_path(struct cx18 *cx)
{ {
struct cx18_av_state *state = &cx->av_state; struct cx18_av_state *state = &cx->av_state;
u8 v;
/* stop microcontroller */ /* 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 */ /* 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! */ /* Mute everything to prevent the PFFT! */
cx18_av_write(cx, 0x8d3, 0x1f); cx18_av_write(cx, 0x8d3, 0x1f);
...@@ -240,12 +243,14 @@ void cx18_av_audio_set_path(struct cx18 *cx) ...@@ -240,12 +243,14 @@ void cx18_av_audio_set_path(struct cx18 *cx)
set_audclk_freq(cx, state->audclk_freq); set_audclk_freq(cx, state->audclk_freq);
/* deassert soft reset */ /* 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) { if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* When the microcontroller detects the /* When the microcontroller detects the
* audio format, it will unmute the lines */ * 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);
} }
} }
...@@ -347,19 +352,23 @@ static int get_mute(struct cx18 *cx) ...@@ -347,19 +352,23 @@ static int get_mute(struct cx18 *cx)
static void set_mute(struct cx18 *cx, int mute) static void set_mute(struct cx18 *cx, int mute)
{ {
struct cx18_av_state *state = &cx->av_state; struct cx18_av_state *state = &cx->av_state;
u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* Must turn off microcontroller in order to mute sound. /* Must turn off microcontroller in order to mute sound.
* Not sure if this is the best method, but it does work. * Not sure if this is the best method, but it does work.
* If the microcontroller is running, then it will undo any * If the microcontroller is running, then it will undo any
* changes to the mute register. */ * changes to the mute register. */
v = cx18_av_read(cx, 0x803);
if (mute) { if (mute) {
/* disable microcontroller */ /* 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); cx18_av_write(cx, 0x8d3, 0x1f);
} else { } else {
/* enable microcontroller */ /* enable microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0x10); v |= 0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
} }
} else { } else {
/* SRC1_MUTE_EN */ /* SRC1_MUTE_EN */
...@@ -375,16 +384,26 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg) ...@@ -375,16 +384,26 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
switch (cmd) { switch (cmd) {
case VIDIOC_INT_AUDIO_CLOCK_FREQ: case VIDIOC_INT_AUDIO_CLOCK_FREQ:
{
u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { 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_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); retval = set_audclk_freq(cx, *(u32 *)arg);
cx18_av_and_or(cx, 0x810, ~0x1, 0);
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) v = cx18_av_read(cx, 0x810) & ~0x1;
cx18_av_and_or(cx, 0x803, ~0x10, 0x10); 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; return retval;
}
case VIDIOC_G_CTRL: case VIDIOC_G_CTRL:
switch (ctrl->id) { switch (ctrl->id) {
......
...@@ -36,12 +36,31 @@ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) ...@@ -36,12 +36,31 @@ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
return 0; 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) int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
{ {
cx18_write_reg(cx, value, 0xc40000 + addr); cx18_write_reg(cx, value, 0xc40000 + addr);
return 0; 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) int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value)
{ {
cx18_write_reg_noretry(cx, value, 0xc40000 + addr); cx18_write_reg_noretry(cx, value, 0xc40000 + addr);
...@@ -98,14 +117,16 @@ static void cx18_av_initialize(struct cx18 *cx) ...@@ -98,14 +117,16 @@ static void cx18_av_initialize(struct cx18 *cx)
cx18_av_loadfw(cx); cx18_av_loadfw(cx);
/* Stop 8051 code execution */ /* 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 */ /* initallize the PLL by toggling sleep bit */
v = cx18_av_read4(cx, CXADEC_HOST_REG1); v = cx18_av_read4(cx, CXADEC_HOST_REG1);
/* enable sleep mode */ /* enable sleep mode - register appears to be read only... */
cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1); cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe);
/* disable sleep mode */ /* 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 */ /* initialize DLLs */
v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF; v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
...@@ -125,9 +146,10 @@ static void cx18_av_initialize(struct cx18 *cx) ...@@ -125,9 +146,10 @@ static void cx18_av_initialize(struct cx18 *cx)
v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1; v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
/* enable TUNE_FIL_RST */ /* 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 */ /* 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 */ /* enable 656 output */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00); cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
...@@ -324,6 +346,7 @@ static void input_change(struct cx18 *cx) ...@@ -324,6 +346,7 @@ static void input_change(struct cx18 *cx)
{ {
struct cx18_av_state *state = &cx->av_state; struct cx18_av_state *state = &cx->av_state;
v4l2_std_id std = state->std; v4l2_std_id std = state->std;
u8 v;
/* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ /* 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); cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
...@@ -333,31 +356,34 @@ static void input_change(struct cx18 *cx) ...@@ -333,31 +356,34 @@ static void input_change(struct cx18 *cx)
if (std & V4L2_STD_525_60) { if (std & V4L2_STD_525_60) {
if (std == V4L2_STD_NTSC_M_JP) { if (std == V4L2_STD_NTSC_M_JP) {
/* Japan uses EIAJ audio standard */ /* Japan uses EIAJ audio standard */
cx18_av_write(cx, 0x808, 0xf7); cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff);
cx18_av_write(cx, 0x80b, 0x02); cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f);
} else if (std == V4L2_STD_NTSC_M_KR) { } else if (std == V4L2_STD_NTSC_M_KR) {
/* South Korea uses A2 audio standard */ /* South Korea uses A2 audio standard */
cx18_av_write(cx, 0x808, 0xf8); cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff);
cx18_av_write(cx, 0x80b, 0x03); cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else { } else {
/* Others use the BTSC audio standard */ /* Others use the BTSC audio standard */
cx18_av_write(cx, 0x808, 0xf6); cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff);
cx18_av_write(cx, 0x80b, 0x01); cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f);
} }
} else if (std & V4L2_STD_PAL) { } else if (std & V4L2_STD_PAL) {
/* Follow tuner change procedure for PAL */ /* Follow tuner change procedure for PAL */
cx18_av_write(cx, 0x808, 0xff); cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
cx18_av_write(cx, 0x80b, 0x03); cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else if (std & V4L2_STD_SECAM) { } else if (std & V4L2_STD_SECAM) {
/* Select autodetect for SECAM */ /* Select autodetect for SECAM */
cx18_av_write(cx, 0x808, 0xff); cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
cx18_av_write(cx, 0x80b, 0x03); 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 */ /* restart audio decoder microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0x00); v &= ~0x10;
cx18_av_and_or(cx, 0x803, ~0x10, 0x10); cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
v |= 0x10;
cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
} }
} }
...@@ -368,6 +394,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, ...@@ -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 && u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
vid_input <= CX18_AV_COMPOSITE8); vid_input <= CX18_AV_COMPOSITE8);
u8 reg; u8 reg;
u8 v;
CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n", CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
vid_input, aud_input); vid_input, aud_input);
...@@ -413,16 +440,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, ...@@ -413,16 +440,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
return -EINVAL; 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) */ /* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ /* 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 */ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
cx18_av_and_or(cx, 0x102, ~0x4, 4); v |= 0x4;
else 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);*/ /*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/
state->vid_input = vid_input; state->vid_input = vid_input;
...@@ -799,40 +833,47 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) ...@@ -799,40 +833,47 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
} }
case VIDIOC_S_TUNER: case VIDIOC_S_TUNER:
{
u8 v;
if (state->radio) if (state->radio)
break; break;
v = cx18_av_read(cx, 0x809);
v &= ~0xf;
switch (vt->audmode) { switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO: case V4L2_TUNER_MODE_MONO:
/* mono -> mono /* mono -> mono
stereo -> mono stereo -> mono
bilingual -> lang1 */ bilingual -> lang1 */
cx18_av_and_or(cx, 0x809, ~0xf, 0x00);
break; break;
case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_STEREO:
case V4L2_TUNER_MODE_LANG1: case V4L2_TUNER_MODE_LANG1:
/* mono -> mono /* mono -> mono
stereo -> stereo stereo -> stereo
bilingual -> lang1 */ bilingual -> lang1 */
cx18_av_and_or(cx, 0x809, ~0xf, 0x04); v |= 0x4;
break; break;
case V4L2_TUNER_MODE_LANG1_LANG2: case V4L2_TUNER_MODE_LANG1_LANG2:
/* mono -> mono /* mono -> mono
stereo -> stereo stereo -> stereo
bilingual -> lang1/lang2 */ bilingual -> lang1/lang2 */
cx18_av_and_or(cx, 0x809, ~0xf, 0x07); v |= 0x7;
break; break;
case V4L2_TUNER_MODE_LANG2: case V4L2_TUNER_MODE_LANG2:
/* mono -> mono /* mono -> mono
stereo -> stereo stereo -> stereo
bilingual -> lang2 */ bilingual -> lang2 */
cx18_av_and_or(cx, 0x809, ~0xf, 0x01); v |= 0x1;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
cx18_av_write_expect(cx, 0x809, v, v, 0xff);
state->audmode = vt->audmode; state->audmode = vt->audmode;
break; break;
}
case VIDIOC_G_FMT: case VIDIOC_G_FMT:
return get_v4lfmt(cx, (struct v4l2_format *)arg); return get_v4lfmt(cx, (struct v4l2_format *)arg);
......
...@@ -302,6 +302,9 @@ struct cx18_av_state { ...@@ -302,6 +302,9 @@ struct cx18_av_state {
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); 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(struct cx18 *cx, u16 addr, u32 value);
int cx18_av_write4_noretry(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); u8 cx18_av_read(struct cx18 *cx, u16 addr);
u32 cx18_av_read4(struct cx18 *cx, u16 addr); u32 cx18_av_read4(struct cx18 *cx, u16 addr);
u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr); u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr);
......
...@@ -43,11 +43,13 @@ int cx18_av_loadfw(struct cx18 *cx) ...@@ -43,11 +43,13 @@ int cx18_av_loadfw(struct cx18 *cx)
/* The firmware load often has byte errors, so allow for several /* The firmware load often has byte errors, so allow for several
retries, both at byte level and at the firmware load level. */ retries, both at byte level and at the firmware load level. */
while (retries1 < 5) { while (retries1 < 5) {
cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); 0x00008430, 0xffffffff); /* cx25843 */
cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);
/* Reset the Mako core (Register is undocumented.) */ /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
cx18_av_write4(cx, 0x8100, 0x00010000); cx18_av_write4_expect(cx, 0x8100, 0x00010000,
0x00008430, 0xffffffff); /* cx25843 */
/* Put the 8051 in reset and enable firmware upload */ /* Put the 8051 in reset and enable firmware upload */
cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000); cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
...@@ -93,7 +95,8 @@ int cx18_av_loadfw(struct cx18 *cx) ...@@ -93,7 +95,8 @@ int cx18_av_loadfw(struct cx18 *cx)
return -EIO; 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 */ /* Output to the 416 */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000); cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
...@@ -118,7 +121,8 @@ int cx18_av_loadfw(struct cx18 *cx) ...@@ -118,7 +121,8 @@ int cx18_av_loadfw(struct cx18 *cx)
passthrough */ passthrough */
cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687); 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); */ /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */ /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
...@@ -136,7 +140,7 @@ int cx18_av_loadfw(struct cx18 *cx) ...@@ -136,7 +140,7 @@ int cx18_av_loadfw(struct cx18 *cx)
v |= 0xFF; /* Auto by default */ v |= 0xFF; /* Auto by default */
v |= 0x400; /* Stereo by default */ v |= 0x400; /* Stereo by default */
v |= 0x14000000; 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); release_firmware(fw);
......
...@@ -200,8 +200,10 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) ...@@ -200,8 +200,10 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
void cx18_halt_firmware(struct cx18 *cx) void cx18_halt_firmware(struct cx18 *cx)
{ {
CX18_DEBUG_INFO("Preparing for firmware halt.\n"); CX18_DEBUG_INFO("Preparing for firmware halt.\n");
cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL); 0x0000000F, 0x000F000F);
cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
0x00000002, 0x00020002);
} }
void cx18_init_power(struct cx18 *cx, int lowpwr) void cx18_init_power(struct cx18 *cx, int lowpwr)
...@@ -211,7 +213,8 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) ...@@ -211,7 +213,8 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
/* ADEC out of sleep */ /* ADEC out of sleep */
cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL); cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
0x00000000, 0x00020002);
/* The fast clock is at 200/245 MHz */ /* The fast clock is at 200/245 MHz */
cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
...@@ -248,22 +251,34 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) ...@@ -248,22 +251,34 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
/* VFC = disabled */ /* VFC = disabled */
/* USB = disabled */ /* USB = disabled */
cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004, if (lowpwr) {
CX18_CLOCK_SELECT1); cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006, 0x00000020, 0xFFFFFFFF);
CX18_CLOCK_SELECT2); cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
0x00000004, 0xFFFFFFFF);
cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1); } else {
cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2); /* This doesn't explicitly set every clock select */
cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
0x00000004, 0x00060006);
cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
0x00000006, 0x00060006);
}
cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1); cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2); 0x00000002, 0xFFFFFFFF);
cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
0x00000104, 0xFFFFFFFF);
cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
0x00009026, 0xFFFFFFFF);
cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
0x00003105, 0xFFFFFFFF);
} }
void cx18_init_memory(struct cx18 *cx) void cx18_init_memory(struct cx18 *cx)
{ {
cx18_msleep_timeout(10, 0); cx18_msleep_timeout(10, 0);
cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET); cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
0x00000000, 0x00010001);
cx18_msleep_timeout(10, 0); cx18_msleep_timeout(10, 0);
cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
...@@ -282,13 +297,15 @@ void cx18_init_memory(struct cx18 *cx) ...@@ -282,13 +297,15 @@ void cx18_init_memory(struct cx18 *cx)
cx18_msleep_timeout(10, 0); cx18_msleep_timeout(10, 0);
cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET); cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
0x00000000, 0x00020002);
cx18_msleep_timeout(10, 0); cx18_msleep_timeout(10, 0);
/* use power-down mode when idle */ /* use power-down mode when idle */
cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN); cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
0x00000001, 0x00010001);
cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
...@@ -310,7 +327,9 @@ int cx18_firmware_init(struct cx18 *cx) ...@@ -310,7 +327,9 @@ int cx18_firmware_init(struct cx18 *cx)
/* Allow chip to control CLKRUN */ /* Allow chip to control CLKRUN */
cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ /* Stop the firmware */
cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
0x0000000F, 0x000F000F);
cx18_msleep_timeout(1, 0); cx18_msleep_timeout(1, 0);
...@@ -325,7 +344,8 @@ int cx18_firmware_init(struct cx18 *cx) ...@@ -325,7 +344,8 @@ int cx18_firmware_init(struct cx18 *cx)
cx18_write_enc(cx, 0xE51FF004, 0); cx18_write_enc(cx, 0xE51FF004, 0);
cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */ cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */
/* Start APU */ /* Start APU */
cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET); cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET,
0x00000000, 0x00010001);
cx18_msleep_timeout(500, 0); cx18_msleep_timeout(500, 0);
sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
...@@ -335,7 +355,9 @@ int cx18_firmware_init(struct cx18 *cx) ...@@ -335,7 +355,9 @@ int cx18_firmware_init(struct cx18 *cx)
int retries = 0; int retries = 0;
/* start the CPU */ /* start the CPU */
cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET); cx18_write_reg_expect(cx,
0x00080000, CX18_PROC_SOFT_RESET,
0x00000000, 0x00080008);
while (retries++ < 50) { /* Loop for max 500mS */ while (retries++ < 50) { /* Loop for max 500mS */
if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET)
& 1) == 0) & 1) == 0)
...@@ -352,6 +374,6 @@ int cx18_firmware_init(struct cx18 *cx) ...@@ -352,6 +374,6 @@ int cx18_firmware_init(struct cx18 *cx)
return -EIO; return -EIO;
} }
/* initialize GPIO */ /* initialize GPIO */
cx18_write_reg(cx, 0x14001400, 0xC78110); cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
return 0; return 0;
} }
...@@ -47,15 +47,21 @@ ...@@ -47,15 +47,21 @@
static void gpio_write(struct cx18 *cx) static void gpio_write(struct cx18 *cx)
{ {
u32 dir = cx->gpio_dir; u32 dir_lo = cx->gpio_dir & 0xffff;
u32 val = cx->gpio_val; u32 val_lo = cx->gpio_val & 0xffff;
u32 dir_hi = cx->gpio_dir >> 16;
cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); u32 val_hi = cx->gpio_val >> 16;
cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff),
CX18_REG_GPIO_OUT1); cx18_write_reg_expect(cx, dir_lo << 16,
cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2); CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo);
cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16), cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo,
CX18_REG_GPIO_OUT2); CX18_REG_GPIO_OUT1, val_lo, dir_lo);
cx18_write_reg_expect(cx, dir_hi << 16,
CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi);
cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi,
CX18_REG_GPIO_OUT2, val_hi, dir_hi);
if (!cx18_retry_mmio)
(void) cx18_read_reg(cx, CX18_REG_GPIO_OUT2); /* sync */
} }
void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "cx18-gpio.h" #include "cx18-gpio.h"
#include "cx18-av-core.h" #include "cx18-av-core.h"
#include "cx18-i2c.h" #include "cx18-i2c.h"
#include "cx18-irq.h"
#define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_WR 0xf15000
#define CX18_REG_I2C_1_RD 0xf15008 #define CX18_REG_I2C_1_RD 0xf15008
...@@ -396,22 +397,31 @@ int init_cx18_i2c(struct cx18 *cx) ...@@ -396,22 +397,31 @@ int init_cx18_i2c(struct cx18 *cx)
if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) { if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
/* Reset/Unreset I2C hardware block */ /* Reset/Unreset I2C hardware block */
/* Clock select 220MHz */ /* Clock select 220MHz */
cx18_write_reg(cx, 0x10000000, 0xc71004); cx18_write_reg_expect(cx, 0x10000000, 0xc71004,
0x00000000, 0x10001000);
/* Clock Enable */ /* Clock Enable */
cx18_write_reg_sync(cx, 0x10001000, 0xc71024); cx18_write_reg_expect(cx, 0x10001000, 0xc71024,
0x00001000, 0x10001000);
} }
/* courtesy of Steven Toth <stoth@hauppauge.com> */ /* courtesy of Steven Toth <stoth@hauppauge.com> */
cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
if (!cx18_retry_mmio)
(void) cx18_read_reg(cx, 0xc7001c); /* sync */
mdelay(10); mdelay(10);
cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c); cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0);
if (!cx18_retry_mmio)
(void) cx18_read_reg(cx, 0xc7001c); /* sync */
mdelay(10); mdelay(10);
cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
if (!cx18_retry_mmio)
(void) cx18_read_reg(cx, 0xc7001c); /* sync */
mdelay(10); mdelay(10);
/* Set to edge-triggered intrs. */ /* Set to edge-triggered intrs. */
cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8); cx18_write_reg(cx, 0x00c00000, 0xc730c8);
/* Clear any stale intrs */ /* Clear any stale intrs */
cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4); cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS,
~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT);
/* Hw I2C1 Clock Freq ~100kHz */ /* Hw I2C1 Clock Freq ~100kHz */
cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR); cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment