162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cx18 firmware functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> 662306a36Sopenharmony_ci * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "cx18-driver.h" 1062306a36Sopenharmony_ci#include "cx18-io.h" 1162306a36Sopenharmony_ci#include "cx18-scb.h" 1262306a36Sopenharmony_ci#include "cx18-irq.h" 1362306a36Sopenharmony_ci#include "cx18-firmware.h" 1462306a36Sopenharmony_ci#include "cx18-cards.h" 1562306a36Sopenharmony_ci#include <linux/firmware.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define CX18_PROC_SOFT_RESET 0xc70010 1862306a36Sopenharmony_ci#define CX18_DDR_SOFT_RESET 0xc70014 1962306a36Sopenharmony_ci#define CX18_CLOCK_SELECT1 0xc71000 2062306a36Sopenharmony_ci#define CX18_CLOCK_SELECT2 0xc71004 2162306a36Sopenharmony_ci#define CX18_HALF_CLOCK_SELECT1 0xc71008 2262306a36Sopenharmony_ci#define CX18_HALF_CLOCK_SELECT2 0xc7100C 2362306a36Sopenharmony_ci#define CX18_CLOCK_POLARITY1 0xc71010 2462306a36Sopenharmony_ci#define CX18_CLOCK_POLARITY2 0xc71014 2562306a36Sopenharmony_ci#define CX18_ADD_DELAY_ENABLE1 0xc71018 2662306a36Sopenharmony_ci#define CX18_ADD_DELAY_ENABLE2 0xc7101C 2762306a36Sopenharmony_ci#define CX18_CLOCK_ENABLE1 0xc71020 2862306a36Sopenharmony_ci#define CX18_CLOCK_ENABLE2 0xc71024 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define CX18_REG_BUS_TIMEOUT_EN 0xc72024 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_INT 0xc78000 3362306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_FRAC 0xc78004 3462306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_POST 0xc78008 3562306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C 3662306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define CX18_SLOW_CLOCK_PLL_INT 0xc78014 3962306a36Sopenharmony_ci#define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018 4062306a36Sopenharmony_ci#define CX18_SLOW_CLOCK_PLL_POST 0xc7801C 4162306a36Sopenharmony_ci#define CX18_MPEG_CLOCK_PLL_INT 0xc78040 4262306a36Sopenharmony_ci#define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044 4362306a36Sopenharmony_ci#define CX18_MPEG_CLOCK_PLL_POST 0xc78048 4462306a36Sopenharmony_ci#define CX18_PLL_POWER_DOWN 0xc78088 4562306a36Sopenharmony_ci#define CX18_SW1_INT_STATUS 0xc73104 4662306a36Sopenharmony_ci#define CX18_SW1_INT_ENABLE_PCI 0xc7311C 4762306a36Sopenharmony_ci#define CX18_SW2_INT_SET 0xc73140 4862306a36Sopenharmony_ci#define CX18_SW2_INT_STATUS 0xc73144 4962306a36Sopenharmony_ci#define CX18_ADEC_CONTROL 0xc78120 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define CX18_DDR_REQUEST_ENABLE 0xc80000 5262306a36Sopenharmony_ci#define CX18_DDR_CHIP_CONFIG 0xc80004 5362306a36Sopenharmony_ci#define CX18_DDR_REFRESH 0xc80008 5462306a36Sopenharmony_ci#define CX18_DDR_TIMING1 0xc8000C 5562306a36Sopenharmony_ci#define CX18_DDR_TIMING2 0xc80010 5662306a36Sopenharmony_ci#define CX18_DDR_POWER_REG 0xc8001C 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define CX18_DDR_TUNE_LANE 0xc80048 5962306a36Sopenharmony_ci#define CX18_DDR_INITIAL_EMRS 0xc80054 6062306a36Sopenharmony_ci#define CX18_DDR_MB_PER_ROW_7 0xc8009C 6162306a36Sopenharmony_ci#define CX18_DDR_BASE_63_ADDR 0xc804FC 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define CX18_WMB_CLIENT02 0xc90108 6462306a36Sopenharmony_ci#define CX18_WMB_CLIENT05 0xc90114 6562306a36Sopenharmony_ci#define CX18_WMB_CLIENT06 0xc90118 6662306a36Sopenharmony_ci#define CX18_WMB_CLIENT07 0xc9011C 6762306a36Sopenharmony_ci#define CX18_WMB_CLIENT08 0xc90120 6862306a36Sopenharmony_ci#define CX18_WMB_CLIENT09 0xc90124 6962306a36Sopenharmony_ci#define CX18_WMB_CLIENT10 0xc90128 7062306a36Sopenharmony_ci#define CX18_WMB_CLIENT11 0xc9012C 7162306a36Sopenharmony_ci#define CX18_WMB_CLIENT12 0xc90130 7262306a36Sopenharmony_ci#define CX18_WMB_CLIENT13 0xc90134 7362306a36Sopenharmony_ci#define CX18_WMB_CLIENT14 0xc90138 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define CX18_DSP0_INTERRUPT_MASK 0xd0004C 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ 7862306a36Sopenharmony_ci#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct cx18_apu_rom_seghdr { 8162306a36Sopenharmony_ci u32 sync1; 8262306a36Sopenharmony_ci u32 sync2; 8362306a36Sopenharmony_ci u32 addr; 8462306a36Sopenharmony_ci u32 size; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci const struct firmware *fw = NULL; 9062306a36Sopenharmony_ci int i, j; 9162306a36Sopenharmony_ci unsigned size; 9262306a36Sopenharmony_ci u32 __iomem *dst = (u32 __iomem *)mem; 9362306a36Sopenharmony_ci const u32 *src; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { 9662306a36Sopenharmony_ci CX18_ERR("Unable to open firmware %s\n", fn); 9762306a36Sopenharmony_ci CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); 9862306a36Sopenharmony_ci return -ENOMEM; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci src = (const u32 *)fw->data; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci for (i = 0; i < fw->size; i += 4096) { 10462306a36Sopenharmony_ci cx18_setup_page(cx, i); 10562306a36Sopenharmony_ci for (j = i; j < fw->size && j < i + 4096; j += 4) { 10662306a36Sopenharmony_ci /* no need for endianness conversion on the ppc */ 10762306a36Sopenharmony_ci cx18_raw_writel(cx, *src, dst); 10862306a36Sopenharmony_ci if (cx18_raw_readl(cx, dst) != *src) { 10962306a36Sopenharmony_ci CX18_ERR("Mismatch at offset %x\n", i); 11062306a36Sopenharmony_ci release_firmware(fw); 11162306a36Sopenharmony_ci cx18_setup_page(cx, 0); 11262306a36Sopenharmony_ci return -EIO; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci dst++; 11562306a36Sopenharmony_ci src++; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) 11962306a36Sopenharmony_ci CX18_INFO("loaded %s firmware (%zu bytes)\n", fn, fw->size); 12062306a36Sopenharmony_ci size = fw->size; 12162306a36Sopenharmony_ci release_firmware(fw); 12262306a36Sopenharmony_ci cx18_setup_page(cx, SCB_OFFSET); 12362306a36Sopenharmony_ci return size; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, 12762306a36Sopenharmony_ci u32 *entry_addr) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci const struct firmware *fw = NULL; 13062306a36Sopenharmony_ci int i, j; 13162306a36Sopenharmony_ci unsigned size; 13262306a36Sopenharmony_ci const u32 *src; 13362306a36Sopenharmony_ci struct cx18_apu_rom_seghdr seghdr; 13462306a36Sopenharmony_ci const u8 *vers; 13562306a36Sopenharmony_ci u32 offset = 0; 13662306a36Sopenharmony_ci u32 apu_version = 0; 13762306a36Sopenharmony_ci int sz; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { 14062306a36Sopenharmony_ci CX18_ERR("unable to open firmware %s\n", fn); 14162306a36Sopenharmony_ci CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); 14262306a36Sopenharmony_ci cx18_setup_page(cx, 0); 14362306a36Sopenharmony_ci return -ENOMEM; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci *entry_addr = 0; 14762306a36Sopenharmony_ci src = (const u32 *)fw->data; 14862306a36Sopenharmony_ci vers = fw->data + sizeof(seghdr); 14962306a36Sopenharmony_ci sz = fw->size; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; 15262306a36Sopenharmony_ci while (offset + sizeof(seghdr) < fw->size) { 15362306a36Sopenharmony_ci const __le32 *shptr = (__force __le32 *)src + offset / 4; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci seghdr.sync1 = le32_to_cpu(shptr[0]); 15662306a36Sopenharmony_ci seghdr.sync2 = le32_to_cpu(shptr[1]); 15762306a36Sopenharmony_ci seghdr.addr = le32_to_cpu(shptr[2]); 15862306a36Sopenharmony_ci seghdr.size = le32_to_cpu(shptr[3]); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci offset += sizeof(seghdr); 16162306a36Sopenharmony_ci if (seghdr.sync1 != APU_ROM_SYNC1 || 16262306a36Sopenharmony_ci seghdr.sync2 != APU_ROM_SYNC2) { 16362306a36Sopenharmony_ci offset += seghdr.size; 16462306a36Sopenharmony_ci continue; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, 16762306a36Sopenharmony_ci seghdr.addr + seghdr.size - 1); 16862306a36Sopenharmony_ci if (*entry_addr == 0) 16962306a36Sopenharmony_ci *entry_addr = seghdr.addr; 17062306a36Sopenharmony_ci if (offset + seghdr.size > sz) 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci for (i = 0; i < seghdr.size; i += 4096) { 17362306a36Sopenharmony_ci cx18_setup_page(cx, seghdr.addr + i); 17462306a36Sopenharmony_ci for (j = i; j < seghdr.size && j < i + 4096; j += 4) { 17562306a36Sopenharmony_ci /* no need for endianness conversion on the ppc */ 17662306a36Sopenharmony_ci cx18_raw_writel(cx, src[(offset + j) / 4], 17762306a36Sopenharmony_ci dst + seghdr.addr + j); 17862306a36Sopenharmony_ci if (cx18_raw_readl(cx, dst + seghdr.addr + j) 17962306a36Sopenharmony_ci != src[(offset + j) / 4]) { 18062306a36Sopenharmony_ci CX18_ERR("Mismatch at offset %x\n", 18162306a36Sopenharmony_ci offset + j); 18262306a36Sopenharmony_ci release_firmware(fw); 18362306a36Sopenharmony_ci cx18_setup_page(cx, 0); 18462306a36Sopenharmony_ci return -EIO; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci offset += seghdr.size; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) 19162306a36Sopenharmony_ci CX18_INFO("loaded %s firmware V%08x (%zu bytes)\n", 19262306a36Sopenharmony_ci fn, apu_version, fw->size); 19362306a36Sopenharmony_ci size = fw->size; 19462306a36Sopenharmony_ci release_firmware(fw); 19562306a36Sopenharmony_ci cx18_setup_page(cx, 0); 19662306a36Sopenharmony_ci return size; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_civoid cx18_halt_firmware(struct cx18 *cx) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci CX18_DEBUG_INFO("Preparing for firmware halt.\n"); 20262306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, 20362306a36Sopenharmony_ci 0x0000000F, 0x000F000F); 20462306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL, 20562306a36Sopenharmony_ci 0x00000002, 0x00020002); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_civoid cx18_init_power(struct cx18 *cx, int lowpwr) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci /* power-down Spare and AOM PLLs */ 21162306a36Sopenharmony_ci /* power-up fast, slow and mpeg PLLs */ 21262306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* ADEC out of sleep */ 21562306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL, 21662306a36Sopenharmony_ci 0x00000000, 0x00020002); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * The PLL parameters are based on the external crystal frequency that 22062306a36Sopenharmony_ci * would ideally be: 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * NTSC Color subcarrier freq * 8 = 22362306a36Sopenharmony_ci * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * The accidents of history and rationale that explain from where this 22662306a36Sopenharmony_ci * combination of magic numbers originate can be found in: 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in 22962306a36Sopenharmony_ci * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the 23262306a36Sopenharmony_ci * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * As Mike Bradley has rightly pointed out, it's not the exact crystal 23562306a36Sopenharmony_ci * frequency that matters, only that all parts of the driver and 23662306a36Sopenharmony_ci * firmware are using the same value (close to the ideal value). 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * Since I have a strong suspicion that, if the firmware ever assumes a 23962306a36Sopenharmony_ci * crystal value at all, it will assume 28.636360 MHz, the crystal 24062306a36Sopenharmony_ci * freq used in calculations in this driver will be: 24162306a36Sopenharmony_ci * 24262306a36Sopenharmony_ci * xtal_freq = 28.636360 MHz 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * an error of less than 0.13 ppm which is way, way better than any off 24562306a36Sopenharmony_ci * the shelf crystal will have for accuracy anyway. 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Below I aim to run the PLLs' VCOs near 400 MHz to minimize errors. 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * Many thanks to Jeff Campbell and Mike Bradley for their extensive 25062306a36Sopenharmony_ci * investigation, experimentation, testing, and suggested solutions of 25162306a36Sopenharmony_ci * audio/video sync problems with SVideo and CVBS captures. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* the fast clock is at 200/245 MHz */ 25562306a36Sopenharmony_ci /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/ 25662306a36Sopenharmony_ci /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/ 25762306a36Sopenharmony_ci cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); 25862306a36Sopenharmony_ci cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, 25962306a36Sopenharmony_ci CX18_FAST_CLOCK_PLL_FRAC); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST); 26262306a36Sopenharmony_ci cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE); 26362306a36Sopenharmony_ci cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* set slow clock to 125/120 MHz */ 26662306a36Sopenharmony_ci /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */ 26762306a36Sopenharmony_ci /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */ 26862306a36Sopenharmony_ci cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT); 26962306a36Sopenharmony_ci cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F, 27062306a36Sopenharmony_ci CX18_SLOW_CLOCK_PLL_FRAC); 27162306a36Sopenharmony_ci cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* mpeg clock pll 54MHz */ 27462306a36Sopenharmony_ci /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */ 27562306a36Sopenharmony_ci cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); 27662306a36Sopenharmony_ci cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC); 27762306a36Sopenharmony_ci cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Defaults */ 28062306a36Sopenharmony_ci /* APU = SC or SC/2 = 125/62.5 */ 28162306a36Sopenharmony_ci /* EPU = SC = 125 */ 28262306a36Sopenharmony_ci /* DDR = FC = 180 */ 28362306a36Sopenharmony_ci /* ENC = SC = 125 */ 28462306a36Sopenharmony_ci /* AI1 = SC = 125 */ 28562306a36Sopenharmony_ci /* VIM2 = disabled */ 28662306a36Sopenharmony_ci /* PCI = FC/2 = 90 */ 28762306a36Sopenharmony_ci /* AI2 = disabled */ 28862306a36Sopenharmony_ci /* DEMUX = disabled */ 28962306a36Sopenharmony_ci /* AO = SC/2 = 62.5 */ 29062306a36Sopenharmony_ci /* SER = 54MHz */ 29162306a36Sopenharmony_ci /* VFC = disabled */ 29262306a36Sopenharmony_ci /* USB = disabled */ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (lowpwr) { 29562306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1, 29662306a36Sopenharmony_ci 0x00000020, 0xFFFFFFFF); 29762306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2, 29862306a36Sopenharmony_ci 0x00000004, 0xFFFFFFFF); 29962306a36Sopenharmony_ci } else { 30062306a36Sopenharmony_ci /* This doesn't explicitly set every clock select */ 30162306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1, 30262306a36Sopenharmony_ci 0x00000004, 0x00060006); 30362306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2, 30462306a36Sopenharmony_ci 0x00000006, 0x00060006); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1, 30862306a36Sopenharmony_ci 0x00000002, 0xFFFFFFFF); 30962306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2, 31062306a36Sopenharmony_ci 0x00000104, 0xFFFFFFFF); 31162306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1, 31262306a36Sopenharmony_ci 0x00009026, 0xFFFFFFFF); 31362306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2, 31462306a36Sopenharmony_ci 0x00003105, 0xFFFFFFFF); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_civoid cx18_init_memory(struct cx18 *cx) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci cx18_msleep_timeout(10, 0); 32062306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET, 32162306a36Sopenharmony_ci 0x00000000, 0x00010001); 32262306a36Sopenharmony_ci cx18_msleep_timeout(10, 0); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci cx18_msleep_timeout(10, 0); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH); 32962306a36Sopenharmony_ci cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1); 33062306a36Sopenharmony_ci cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci cx18_msleep_timeout(10, 0); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Initialize DQS pad time */ 33562306a36Sopenharmony_ci cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); 33662306a36Sopenharmony_ci cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci cx18_msleep_timeout(10, 0); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET, 34162306a36Sopenharmony_ci 0x00000000, 0x00020002); 34262306a36Sopenharmony_ci cx18_msleep_timeout(10, 0); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* use power-down mode when idle */ 34562306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN, 34862306a36Sopenharmony_ci 0x00000001, 0x00010001); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); 35162306a36Sopenharmony_ci cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */ 35462306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */ 35562306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ 35662306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */ 35762306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ 35862306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */ 35962306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */ 36062306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */ 36162306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */ 36262306a36Sopenharmony_ci cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */ 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci#define CX18_CPU_FIRMWARE "v4l-cx23418-cpu.fw" 36662306a36Sopenharmony_ci#define CX18_APU_FIRMWARE "v4l-cx23418-apu.fw" 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ciint cx18_firmware_init(struct cx18 *cx) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci u32 fw_entry_addr; 37162306a36Sopenharmony_ci int sz, retries; 37262306a36Sopenharmony_ci u32 api_args[MAX_MB_ARGUMENTS]; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Allow chip to control CLKRUN */ 37562306a36Sopenharmony_ci cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Stop the firmware */ 37862306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, 37962306a36Sopenharmony_ci 0x0000000F, 0x000F000F); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci cx18_msleep_timeout(1, 0); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* If the CPU is still running */ 38462306a36Sopenharmony_ci if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) { 38562306a36Sopenharmony_ci CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__); 38662306a36Sopenharmony_ci return -EIO; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); 39062306a36Sopenharmony_ci cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci sz = load_cpu_fw_direct(CX18_CPU_FIRMWARE, cx->enc_mem, cx); 39362306a36Sopenharmony_ci if (sz <= 0) 39462306a36Sopenharmony_ci return sz; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* The SCB & IPC area *must* be correct before starting the firmwares */ 39762306a36Sopenharmony_ci cx18_init_scb(cx); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci fw_entry_addr = 0; 40062306a36Sopenharmony_ci sz = load_apu_fw_direct(CX18_APU_FIRMWARE, cx->enc_mem, cx, 40162306a36Sopenharmony_ci &fw_entry_addr); 40262306a36Sopenharmony_ci if (sz <= 0) 40362306a36Sopenharmony_ci return sz; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* Start the CPU. The CPU will take care of the APU for us. */ 40662306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET, 40762306a36Sopenharmony_ci 0x00000000, 0x00080008); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Wait up to 500 ms for the APU to come out of reset */ 41062306a36Sopenharmony_ci for (retries = 0; 41162306a36Sopenharmony_ci retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1; 41262306a36Sopenharmony_ci retries++) 41362306a36Sopenharmony_ci cx18_msleep_timeout(10, 0); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci cx18_msleep_timeout(200, 0); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (retries == 50 && 41862306a36Sopenharmony_ci (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) { 41962306a36Sopenharmony_ci CX18_ERR("Could not start the CPU\n"); 42062306a36Sopenharmony_ci return -EIO; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * The CPU had once before set up to receive an interrupt for it's 42562306a36Sopenharmony_ci * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an 42662306a36Sopenharmony_ci * interrupt when it sends us an ack, but by the time we process it, 42762306a36Sopenharmony_ci * that flag in the SW2 status register has been cleared by the CPU 42862306a36Sopenharmony_ci * firmware. We'll prevent that not so useful condition from happening 42962306a36Sopenharmony_ci * by clearing the CPU's interrupt enables for Ack IRQ's we want to 43062306a36Sopenharmony_ci * process. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Try a benign command to see if the CPU is alive and well */ 43562306a36Sopenharmony_ci sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0); 43662306a36Sopenharmony_ci if (sz < 0) 43762306a36Sopenharmony_ci return sz; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* initialize GPIO */ 44062306a36Sopenharmony_ci cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ciMODULE_FIRMWARE(CX18_CPU_FIRMWARE); 44562306a36Sopenharmony_ciMODULE_FIRMWARE(CX18_APU_FIRMWARE); 446