| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  cx18 firmware functions | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl> | 
					
						
							| 
									
										
										
										
											2010-05-23 18:53:35 -03:00
										 |  |  |  *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net> | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  |  * | 
					
						
							|  |  |  |  *  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., 59 Temple Place, Suite 330, Boston, MA | 
					
						
							|  |  |  |  *  02111-1307  USA | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "cx18-driver.h"
 | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | #include "cx18-io.h"
 | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | #include "cx18-scb.h"
 | 
					
						
							|  |  |  | #include "cx18-irq.h"
 | 
					
						
							|  |  |  | #include "cx18-firmware.h"
 | 
					
						
							|  |  |  | #include "cx18-cards.h"
 | 
					
						
							|  |  |  | #include <linux/firmware.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX18_PROC_SOFT_RESET 		0xc70010
 | 
					
						
							|  |  |  | #define CX18_DDR_SOFT_RESET          	0xc70014
 | 
					
						
							|  |  |  | #define CX18_CLOCK_SELECT1           	0xc71000
 | 
					
						
							|  |  |  | #define CX18_CLOCK_SELECT2           	0xc71004
 | 
					
						
							|  |  |  | #define CX18_HALF_CLOCK_SELECT1      	0xc71008
 | 
					
						
							|  |  |  | #define CX18_HALF_CLOCK_SELECT2      	0xc7100C
 | 
					
						
							|  |  |  | #define CX18_CLOCK_POLARITY1         	0xc71010
 | 
					
						
							|  |  |  | #define CX18_CLOCK_POLARITY2         	0xc71014
 | 
					
						
							|  |  |  | #define CX18_ADD_DELAY_ENABLE1       	0xc71018
 | 
					
						
							|  |  |  | #define CX18_ADD_DELAY_ENABLE2       	0xc7101C
 | 
					
						
							|  |  |  | #define CX18_CLOCK_ENABLE1           	0xc71020
 | 
					
						
							|  |  |  | #define CX18_CLOCK_ENABLE2           	0xc71024
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX18_REG_BUS_TIMEOUT_EN      	0xc72024
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX18_FAST_CLOCK_PLL_INT      	0xc78000
 | 
					
						
							|  |  |  | #define CX18_FAST_CLOCK_PLL_FRAC     	0xc78004
 | 
					
						
							|  |  |  | #define CX18_FAST_CLOCK_PLL_POST     	0xc78008
 | 
					
						
							|  |  |  | #define CX18_FAST_CLOCK_PLL_PRESCALE 	0xc7800C
 | 
					
						
							|  |  |  | #define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX18_SLOW_CLOCK_PLL_INT      	0xc78014
 | 
					
						
							|  |  |  | #define CX18_SLOW_CLOCK_PLL_FRAC     	0xc78018
 | 
					
						
							|  |  |  | #define CX18_SLOW_CLOCK_PLL_POST     	0xc7801C
 | 
					
						
							|  |  |  | #define CX18_MPEG_CLOCK_PLL_INT		0xc78040
 | 
					
						
							|  |  |  | #define CX18_MPEG_CLOCK_PLL_FRAC	0xc78044
 | 
					
						
							|  |  |  | #define CX18_MPEG_CLOCK_PLL_POST	0xc78048
 | 
					
						
							|  |  |  | #define CX18_PLL_POWER_DOWN          	0xc78088
 | 
					
						
							|  |  |  | #define CX18_SW1_INT_STATUS             0xc73104
 | 
					
						
							|  |  |  | #define CX18_SW1_INT_ENABLE_PCI         0xc7311C
 | 
					
						
							|  |  |  | #define CX18_SW2_INT_SET                0xc73140
 | 
					
						
							|  |  |  | #define CX18_SW2_INT_STATUS             0xc73144
 | 
					
						
							|  |  |  | #define CX18_ADEC_CONTROL            	0xc78120
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX18_DDR_REQUEST_ENABLE      	0xc80000
 | 
					
						
							|  |  |  | #define CX18_DDR_CHIP_CONFIG         	0xc80004
 | 
					
						
							|  |  |  | #define CX18_DDR_REFRESH            	0xc80008
 | 
					
						
							|  |  |  | #define CX18_DDR_TIMING1             	0xc8000C
 | 
					
						
							|  |  |  | #define CX18_DDR_TIMING2             	0xc80010
 | 
					
						
							|  |  |  | #define CX18_DDR_POWER_REG		0xc8001C
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX18_DDR_TUNE_LANE           	0xc80048
 | 
					
						
							|  |  |  | #define CX18_DDR_INITIAL_EMRS        	0xc80054
 | 
					
						
							|  |  |  | #define CX18_DDR_MB_PER_ROW_7        	0xc8009C
 | 
					
						
							|  |  |  | #define CX18_DDR_BASE_63_ADDR        	0xc804FC
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT02            	0xc90108
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT05            	0xc90114
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT06            	0xc90118
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT07            	0xc9011C
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT08            	0xc90120
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT09            	0xc90124
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT10            	0xc90128
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT11            	0xc9012C
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT12            	0xc90130
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT13            	0xc90134
 | 
					
						
							|  |  |  | #define CX18_WMB_CLIENT14            	0xc90138
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX18_DSP0_INTERRUPT_MASK     	0xd0004C
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
 | 
					
						
							|  |  |  | #define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct cx18_apu_rom_seghdr { | 
					
						
							|  |  |  | 	u32 sync1; | 
					
						
							|  |  |  | 	u32 sync2; | 
					
						
							|  |  |  | 	u32 addr; | 
					
						
							|  |  |  | 	u32 size; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-19 08:34:12 -03:00
										 |  |  | static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	const struct firmware *fw = NULL; | 
					
						
							|  |  |  | 	int i, j; | 
					
						
							| 
									
										
										
										
											2008-07-19 08:34:12 -03:00
										 |  |  | 	unsigned size; | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	u32 __iomem *dst = (u32 __iomem *)mem; | 
					
						
							|  |  |  | 	const u32 *src; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-10 21:54:39 -03:00
										 |  |  | 	if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { | 
					
						
							| 
									
										
										
										
											2008-07-19 08:34:12 -03:00
										 |  |  | 		CX18_ERR("Unable to open firmware %s\n", fn); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 		CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	src = (const u32 *)fw->data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < fw->size; i += 4096) { | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 		cx18_setup_page(cx, i); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 		for (j = i; j < fw->size && j < i + 4096; j += 4) { | 
					
						
							|  |  |  | 			/* no need for endianness conversion on the ppc */ | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 			cx18_raw_writel(cx, *src, dst); | 
					
						
							|  |  |  | 			if (cx18_raw_readl(cx, dst) != *src) { | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 				CX18_ERR("Mismatch at offset %x\n", i); | 
					
						
							|  |  |  | 				release_firmware(fw); | 
					
						
							| 
									
										
										
										
											2008-11-16 01:38:19 -03:00
										 |  |  | 				cx18_setup_page(cx, 0); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 				return -EIO; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			dst++; | 
					
						
							|  |  |  | 			src++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) | 
					
						
							| 
									
										
										
										
											2014-09-24 20:35:48 -03:00
										 |  |  | 		CX18_INFO("loaded %s firmware (%zu bytes)\n", fn, fw->size); | 
					
						
							| 
									
										
										
										
											2008-07-19 08:34:12 -03:00
										 |  |  | 	size = fw->size; | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	release_firmware(fw); | 
					
						
							| 
									
										
										
										
											2008-11-16 01:38:19 -03:00
										 |  |  | 	cx18_setup_page(cx, SCB_OFFSET); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	return size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-08 17:14:22 -03:00
										 |  |  | static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, | 
					
						
							|  |  |  | 				u32 *entry_addr) | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	const struct firmware *fw = NULL; | 
					
						
							|  |  |  | 	int i, j; | 
					
						
							| 
									
										
										
										
											2008-07-19 08:34:12 -03:00
										 |  |  | 	unsigned size; | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	const u32 *src; | 
					
						
							|  |  |  | 	struct cx18_apu_rom_seghdr seghdr; | 
					
						
							|  |  |  | 	const u8 *vers; | 
					
						
							|  |  |  | 	u32 offset = 0; | 
					
						
							|  |  |  | 	u32 apu_version = 0; | 
					
						
							|  |  |  | 	int sz; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-10 21:54:39 -03:00
										 |  |  | 	if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { | 
					
						
							| 
									
										
										
										
											2008-07-19 08:34:12 -03:00
										 |  |  | 		CX18_ERR("unable to open firmware %s\n", fn); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 		CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); | 
					
						
							| 
									
										
										
										
											2008-11-16 01:38:19 -03:00
										 |  |  | 		cx18_setup_page(cx, 0); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-09 19:51:44 -03:00
										 |  |  | 	*entry_addr = 0; | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	src = (const u32 *)fw->data; | 
					
						
							|  |  |  | 	vers = fw->data + sizeof(seghdr); | 
					
						
							|  |  |  | 	sz = fw->size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; | 
					
						
							| 
									
										
										
										
											2008-07-19 08:34:12 -03:00
										 |  |  | 	while (offset + sizeof(seghdr) < fw->size) { | 
					
						
							| 
									
										
										
										
											2014-08-21 16:38:04 -03:00
										 |  |  | 		const __le32 *shptr = (__force __le32 *)src + offset / 4; | 
					
						
							| 
									
										
										
										
											2012-05-26 08:07:03 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		seghdr.sync1 = le32_to_cpu(shptr[0]); | 
					
						
							|  |  |  | 		seghdr.sync2 = le32_to_cpu(shptr[1]); | 
					
						
							|  |  |  | 		seghdr.addr = le32_to_cpu(shptr[2]); | 
					
						
							|  |  |  | 		seghdr.size = le32_to_cpu(shptr[3]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 		offset += sizeof(seghdr); | 
					
						
							|  |  |  | 		if (seghdr.sync1 != APU_ROM_SYNC1 || | 
					
						
							|  |  |  | 		    seghdr.sync2 != APU_ROM_SYNC2) { | 
					
						
							|  |  |  | 			offset += seghdr.size; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, | 
					
						
							|  |  |  | 				seghdr.addr + seghdr.size - 1); | 
					
						
							| 
									
										
										
										
											2008-11-09 19:51:44 -03:00
										 |  |  | 		if (*entry_addr == 0) | 
					
						
							| 
									
										
										
										
											2008-11-08 17:14:22 -03:00
										 |  |  | 			*entry_addr = seghdr.addr; | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 		if (offset + seghdr.size > sz) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		for (i = 0; i < seghdr.size; i += 4096) { | 
					
						
							| 
									
										
										
										
											2008-11-08 17:14:22 -03:00
										 |  |  | 			cx18_setup_page(cx, seghdr.addr + i); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 			for (j = i; j < seghdr.size && j < i + 4096; j += 4) { | 
					
						
							|  |  |  | 				/* no need for endianness conversion on the ppc */ | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 				cx18_raw_writel(cx, src[(offset + j) / 4], | 
					
						
							|  |  |  | 						dst + seghdr.addr + j); | 
					
						
							|  |  |  | 				if (cx18_raw_readl(cx, dst + seghdr.addr + j) | 
					
						
							|  |  |  | 				    != src[(offset + j) / 4]) { | 
					
						
							|  |  |  | 					CX18_ERR("Mismatch at offset %x\n", | 
					
						
							|  |  |  | 						 offset + j); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 					release_firmware(fw); | 
					
						
							| 
									
										
										
										
											2008-11-16 01:38:19 -03:00
										 |  |  | 					cx18_setup_page(cx, 0); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 					return -EIO; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		offset += seghdr.size; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) | 
					
						
							| 
									
										
										
										
											2014-09-24 20:35:48 -03:00
										 |  |  | 		CX18_INFO("loaded %s firmware V%08x (%zu bytes)\n", | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 				fn, apu_version, fw->size); | 
					
						
							| 
									
										
										
										
											2008-07-19 08:34:12 -03:00
										 |  |  | 	size = fw->size; | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	release_firmware(fw); | 
					
						
							| 
									
										
										
										
											2008-11-16 01:38:19 -03:00
										 |  |  | 	cx18_setup_page(cx, 0); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	return size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void cx18_halt_firmware(struct cx18 *cx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CX18_DEBUG_INFO("Preparing for firmware halt.\n"); | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, | 
					
						
							|  |  |  | 				  0x0000000F, 0x000F000F); | 
					
						
							|  |  |  | 	cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL, | 
					
						
							|  |  |  | 				  0x00000002, 0x00020002); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void cx18_init_power(struct cx18 *cx, int lowpwr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* power-down Spare and AOM PLLs */ | 
					
						
							|  |  |  | 	/* power-up fast, slow and mpeg PLLs */ | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* ADEC out of sleep */ | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL, | 
					
						
							|  |  |  | 				  0x00000000, 0x00020002); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-20 23:48:57 -03:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The PLL parameters are based on the external crystal frequency that | 
					
						
							|  |  |  | 	 * would ideally be: | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * NTSC Color subcarrier freq * 8 = | 
					
						
							|  |  |  | 	 * 	4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * The accidents of history and rationale that explain from where this | 
					
						
							|  |  |  | 	 * combination of magic numbers originate can be found in: | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in | 
					
						
							|  |  |  | 	 * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the | 
					
						
							|  |  |  | 	 * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * As Mike Bradley has rightly pointed out, it's not the exact crystal | 
					
						
							|  |  |  | 	 * frequency that matters, only that all parts of the driver and | 
					
						
							|  |  |  | 	 * firmware are using the same value (close to the ideal value). | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Since I have a strong suspicion that, if the firmware ever assumes a | 
					
						
							|  |  |  | 	 * crystal value at all, it will assume 28.636360 MHz, the crystal | 
					
						
							|  |  |  | 	 * freq used in calculations in this driver will be: | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 *	xtal_freq = 28.636360 MHz | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * an error of less than 0.13 ppm which is way, way better than any off | 
					
						
							|  |  |  | 	 * the shelf crystal will have for accuracy anyway. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Many thanks to Jeff Campbell and Mike Bradley for their extensive | 
					
						
							|  |  |  | 	 * investigation, experimentation, testing, and suggested solutions of | 
					
						
							|  |  |  | 	 * of audio/video sync problems with SVideo and CVBS captures. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* the fast clock is at 200/245 MHz */ | 
					
						
							|  |  |  | 	/* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/ | 
					
						
							|  |  |  | 	/* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/ | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); | 
					
						
							|  |  |  | 	cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, | 
					
						
							|  |  |  | 						CX18_FAST_CLOCK_PLL_FRAC); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST); | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE); | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* set slow clock to 125/120 MHz */ | 
					
						
							| 
									
										
										
										
											2008-12-20 23:48:57 -03:00
										 |  |  | 	/* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */ | 
					
						
							|  |  |  | 	/* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT); | 
					
						
							|  |  |  | 	cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F, | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 						CX18_SLOW_CLOCK_PLL_FRAC); | 
					
						
							| 
									
										
										
										
											2008-12-20 23:48:57 -03:00
										 |  |  | 	cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* mpeg clock pll 54MHz */ | 
					
						
							| 
									
										
										
										
											2008-12-20 23:48:57 -03:00
										 |  |  | 	/* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */ | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); | 
					
						
							| 
									
										
										
										
											2008-12-20 23:48:57 -03:00
										 |  |  | 	cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC); | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Defaults */ | 
					
						
							|  |  |  | 	/* APU = SC or SC/2 = 125/62.5 */ | 
					
						
							|  |  |  | 	/* EPU = SC = 125 */ | 
					
						
							|  |  |  | 	/* DDR = FC = 180 */ | 
					
						
							|  |  |  | 	/* ENC = SC = 125 */ | 
					
						
							|  |  |  | 	/* AI1 = SC = 125 */ | 
					
						
							|  |  |  | 	/* VIM2 = disabled */ | 
					
						
							|  |  |  | 	/* PCI = FC/2 = 90 */ | 
					
						
							|  |  |  | 	/* AI2 = disabled */ | 
					
						
							|  |  |  | 	/* DEMUX = disabled */ | 
					
						
							|  |  |  | 	/* AO = SC/2 = 62.5 */ | 
					
						
							|  |  |  | 	/* SER = 54MHz */ | 
					
						
							|  |  |  | 	/* VFC = disabled */ | 
					
						
							|  |  |  | 	/* USB = disabled */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	if (lowpwr) { | 
					
						
							|  |  |  | 		cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1, | 
					
						
							|  |  |  | 					  0x00000020, 0xFFFFFFFF); | 
					
						
							|  |  |  | 		cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2, | 
					
						
							|  |  |  | 					  0x00000004, 0xFFFFFFFF); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* 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); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1, | 
					
						
							|  |  |  | 				  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); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void cx18_init_memory(struct cx18 *cx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	cx18_msleep_timeout(10, 0); | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET, | 
					
						
							|  |  |  | 				  0x00000000, 0x00010001); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	cx18_msleep_timeout(10, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	cx18_msleep_timeout(10, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH); | 
					
						
							|  |  |  | 	cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1); | 
					
						
							|  |  |  | 	cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	cx18_msleep_timeout(10, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Initialize DQS pad time */ | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); | 
					
						
							|  |  |  | 	cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	cx18_msleep_timeout(10, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET, | 
					
						
							|  |  |  | 				  0x00000000, 0x00020002); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	cx18_msleep_timeout(10, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* use power-down mode when idle */ | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN, | 
					
						
							|  |  |  | 				  0x00000001, 0x00010001); | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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, 0x00000101, CX18_WMB_CLIENT02);  /* AO */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09);  /* AI2 */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05);  /* VIM1 */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06);  /* AI1 */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07);  /* 3D comb */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10);  /* ME */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12);  /* ENC */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13);  /* PK */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11);  /* RC */ | 
					
						
							|  |  |  | 	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14);  /* AVO */ | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-27 12:45:21 -03:00
										 |  |  | #define CX18_CPU_FIRMWARE "v4l-cx23418-cpu.fw"
 | 
					
						
							|  |  |  | #define CX18_APU_FIRMWARE "v4l-cx23418-apu.fw"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | int cx18_firmware_init(struct cx18 *cx) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-14 21:26:25 -03:00
										 |  |  | 	u32 fw_entry_addr; | 
					
						
							|  |  |  | 	int sz, retries; | 
					
						
							|  |  |  | 	u32 api_args[MAX_MB_ARGUMENTS]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	/* Allow chip to control CLKRUN */ | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	/* Stop the firmware */ | 
					
						
							|  |  |  | 	cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, | 
					
						
							|  |  |  | 				  0x0000000F, 0x000F000F); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	cx18_msleep_timeout(1, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-14 21:26:25 -03:00
										 |  |  | 	/* If the CPU is still running */ | 
					
						
							|  |  |  | 	if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) { | 
					
						
							|  |  |  | 		CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__); | 
					
						
							|  |  |  | 		return -EIO; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-30 16:03:44 -03:00
										 |  |  | 	cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | 
					
						
							|  |  |  | 	cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-27 12:45:21 -03:00
										 |  |  | 	sz = load_cpu_fw_direct(CX18_CPU_FIRMWARE, cx->enc_mem, cx); | 
					
						
							| 
									
										
										
										
											2008-12-14 21:26:25 -03:00
										 |  |  | 	if (sz <= 0) | 
					
						
							|  |  |  | 		return sz; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* The SCB & IPC area *must* be correct before starting the firmwares */ | 
					
						
							|  |  |  | 	cx18_init_scb(cx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fw_entry_addr = 0; | 
					
						
							| 
									
										
										
										
											2012-07-27 12:45:21 -03:00
										 |  |  | 	sz = load_apu_fw_direct(CX18_APU_FIRMWARE, cx->enc_mem, cx, | 
					
						
							| 
									
										
										
										
											2008-12-14 21:26:25 -03:00
										 |  |  | 				&fw_entry_addr); | 
					
						
							|  |  |  | 	if (sz <= 0) | 
					
						
							|  |  |  | 		return sz; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Start the CPU. The CPU will take care of the APU for us. */ | 
					
						
							|  |  |  | 	cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET, | 
					
						
							|  |  |  | 				  0x00000000, 0x00080008); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Wait up to 500 ms for the APU to come out of reset */ | 
					
						
							|  |  |  | 	for (retries = 0; | 
					
						
							|  |  |  | 	     retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1; | 
					
						
							|  |  |  | 	     retries++) | 
					
						
							|  |  |  | 		cx18_msleep_timeout(10, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cx18_msleep_timeout(200, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (retries == 50 && | 
					
						
							|  |  |  | 	    (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) { | 
					
						
							|  |  |  | 		CX18_ERR("Could not start the CPU\n"); | 
					
						
							|  |  |  | 		return -EIO; | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-11-09 18:14:07 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2008-12-14 21:26:25 -03:00
										 |  |  | 	 * The CPU had once before set up to receive an interrupt for it's | 
					
						
							|  |  |  | 	 * outgoing IRQ_CPU_TO_EPU_ACK to us.  If it ever does this, we get an | 
					
						
							|  |  |  | 	 * interrupt when it sends us an ack, but by the time we process it, | 
					
						
							|  |  |  | 	 * that flag in the SW2 status register has been cleared by the CPU | 
					
						
							|  |  |  | 	 * firmware.  We'll prevent that not so useful condition from happening | 
					
						
							|  |  |  | 	 * by clearing the CPU's interrupt enables for Ack IRQ's we want to | 
					
						
							|  |  |  | 	 * process. | 
					
						
							| 
									
										
										
										
											2008-11-09 18:14:07 -03:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-14 21:26:25 -03:00
										 |  |  | 	/* Try a benign command to see if the CPU is alive and well */ | 
					
						
							|  |  |  | 	sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0); | 
					
						
							|  |  |  | 	if (sz < 0) | 
					
						
							|  |  |  | 		return sz; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	/* initialize GPIO */ | 
					
						
							| 
									
										
										
										
											2008-11-02 10:59:04 -03:00
										 |  |  | 	cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); | 
					
						
							| 
									
										
										
										
											2008-04-28 20:24:33 -03:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-07-27 12:45:21 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_FIRMWARE(CX18_CPU_FIRMWARE); | 
					
						
							|  |  |  | MODULE_FIRMWARE(CX18_APU_FIRMWARE); |