diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-av-firmware.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-av-firmware.c | 72 |
1 files changed, 45 insertions, 27 deletions
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index a1a6af6c1c8..834b9248242 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/drivers/media/video/cx18/cx18-av-firmware.c @@ -22,6 +22,7 @@ #include "cx18-driver.h" #include <linux/firmware.h> +#define CX18_AUDIO_ENABLE 0xc72014 #define FWFILE "v4l-cx23418-dig.fw" int cx18_av_loadfw(struct cx18 *cx) @@ -31,40 +32,58 @@ int cx18_av_loadfw(struct cx18 *cx) u32 v; const u8 *ptr; int i; + int retries = 0; if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) { CX18_ERR("unable to open firmware %s\n", FWFILE); return -EINVAL; } - cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); - cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); /* Byte 0 */ - - /* Reset the Mako core (Register is undocumented.) */ - cx18_av_write4(cx, 0x8100, 0x00010000); - - /* Put the 8051 in reset and enable firmware upload */ - cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000); - - ptr = fw->data; - size = fw->size; - - for (i = 0; i < size; i++) { - u32 dl_control = 0x0F000000 | ((u32)ptr[i] << 16); - u32 value = 0; - int retries; - - for (retries = 0; retries < 5; retries++) { - cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); - value = cx18_av_read4(cx, CXADEC_DL_CTL); - if ((value & 0x3F00) == (dl_control & 0x3F00)) + /* The firmware load often has byte errors, so allow for several + retries, both at byte level and at the firmware load level. */ + while (retries < 5) { + cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); + cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); + + /* Reset the Mako core (Register is undocumented.) */ + cx18_av_write4(cx, 0x8100, 0x00010000); + + /* Put the 8051 in reset and enable firmware upload */ + cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000); + + ptr = fw->data; + size = fw->size; + + for (i = 0; i < size; i++) { + u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16); + u32 value = 0; + int retries; + + for (retries = 0; retries < 5; retries++) { + cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); + udelay(10); + value = cx18_av_read4(cx, CXADEC_DL_CTL); + if (value == dl_control) + break; + /* Check if we can correct the byte by changing + the address. We can only write the lower + address byte of the address. */ + if ((value & 0x3F00) != (dl_control & 0x3F00)) { + retries = 5; + break; + } + } + if (retries >= 5) break; } - if (retries >= 5) { - CX18_ERR("unable to load firmware %s\n", FWFILE); - release_firmware(fw); - return -EIO; - } + if (i == size) + break; + retries++; + } + if (retries >= 5) { + CX18_ERR("unable to load firmware %s\n", FWFILE); + release_firmware(fw); + return -EIO; } cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size); @@ -100,7 +119,6 @@ int cx18_av_loadfw(struct cx18 *cx) have a name in the spec. */ cx18_av_write4(cx, 0x09CC, 1); -#define CX18_AUDIO_ENABLE 0xc72014 v = read_reg(CX18_AUDIO_ENABLE); /* If bit 11 is 1 */ if (v & 0x800) |