]>
Commit | Line | Data |
---|---|---|
bd985160 HV |
1 | /* cx25840 firmware functions |
2 | * | |
3 | * This program is free software; you can redistribute it and/or | |
4 | * modify it under the terms of the GNU General Public License | |
5 | * as published by the Free Software Foundation; either version 2 | |
6 | * of the License, or (at your option) any later version. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program; if not, write to the Free Software | |
15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
16 | */ | |
17 | ||
bd985160 HV |
18 | #include <linux/module.h> |
19 | #include <linux/i2c.h> | |
20 | #include <linux/i2c-algo-bit.h> | |
21 | #include <linux/firmware.h> | |
22 | #include <media/v4l2-common.h> | |
31bc09b5 | 23 | #include <media/cx25840.h> |
bd985160 | 24 | |
31bc09b5 | 25 | #include "cx25840-core.h" |
bd985160 | 26 | |
60f6c464 | 27 | #define FWFILE "v4l-cx25840.fw" |
bd985160 HV |
28 | #define FWSEND 1024 |
29 | ||
30 | #define FWDEV(x) &((x)->adapter->dev) | |
31 | ||
32 | static int fastfw = 1; | |
33 | static char *firmware = FWFILE; | |
34 | ||
35 | module_param(fastfw, bool, 0444); | |
36 | module_param(firmware, charp, 0444); | |
37 | ||
38 | MODULE_PARM_DESC(fastfw, "Load firmware fast [0=100MHz 1=333MHz (default)]"); | |
39 | MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]"); | |
40 | ||
d92c20e0 | 41 | static void set_i2c_delay(struct i2c_client *client, int delay) |
bd985160 HV |
42 | { |
43 | struct i2c_algo_bit_data *algod = client->adapter->algo_data; | |
44 | ||
45 | /* We aren't guaranteed to be using algo_bit, | |
46 | * so avoid the null pointer dereference | |
47 | * and disable the 'fast firmware load' */ | |
48 | if (algod) { | |
49 | algod->udelay = delay; | |
50 | } else { | |
51 | fastfw = 0; | |
52 | } | |
53 | } | |
54 | ||
d92c20e0 | 55 | static void start_fw_load(struct i2c_client *client) |
bd985160 HV |
56 | { |
57 | /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ | |
58 | cx25840_write(client, 0x800, 0x00); | |
59 | cx25840_write(client, 0x801, 0x00); | |
60 | // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 | |
61 | cx25840_write(client, 0x803, 0x0b); | |
62 | /* AUTO_INC_DIS=1 */ | |
63 | cx25840_write(client, 0x000, 0x20); | |
64 | ||
65 | if (fastfw) | |
66 | set_i2c_delay(client, 3); | |
67 | } | |
68 | ||
d92c20e0 | 69 | static void end_fw_load(struct i2c_client *client) |
bd985160 HV |
70 | { |
71 | if (fastfw) | |
72 | set_i2c_delay(client, 10); | |
73 | ||
74 | /* AUTO_INC_DIS=0 */ | |
75 | cx25840_write(client, 0x000, 0x00); | |
76 | /* DL_ENABLE=0 */ | |
77 | cx25840_write(client, 0x803, 0x03); | |
78 | } | |
79 | ||
d92c20e0 | 80 | static int check_fw_load(struct i2c_client *client, int size) |
bd985160 HV |
81 | { |
82 | /* DL_ADDR_HB DL_ADDR_LB */ | |
83 | int s = cx25840_read(client, 0x801) << 8; | |
84 | s |= cx25840_read(client, 0x800); | |
85 | ||
86 | if (size != s) { | |
fac9e899 | 87 | v4l_err(client, "firmware %s load failed\n", firmware); |
bd985160 HV |
88 | return -EINVAL; |
89 | } | |
90 | ||
fac9e899 | 91 | v4l_info(client, "loaded %s firmware (%d bytes)\n", firmware, size); |
bd985160 HV |
92 | return 0; |
93 | } | |
94 | ||
d92c20e0 | 95 | static int fw_write(struct i2c_client *client, u8 * data, int size) |
bd985160 | 96 | { |
210e207c TT |
97 | int sent; |
98 | ||
99 | if ((sent = i2c_master_send(client, data, size)) < size) { | |
bd985160 HV |
100 | |
101 | if (fastfw) { | |
fac9e899 | 102 | v4l_err(client, "333MHz i2c firmware load failed\n"); |
bd985160 HV |
103 | fastfw = 0; |
104 | set_i2c_delay(client, 10); | |
105 | ||
210e207c TT |
106 | if (sent > 2) { |
107 | u16 dl_addr = cx25840_read(client, 0x801) << 8; | |
108 | dl_addr |= cx25840_read(client, 0x800); | |
109 | dl_addr -= sent - 2; | |
110 | cx25840_write(client, 0x801, dl_addr >> 8); | |
111 | cx25840_write(client, 0x800, dl_addr & 0xff); | |
112 | } | |
113 | ||
bd985160 | 114 | if (i2c_master_send(client, data, size) < size) { |
fac9e899 | 115 | v4l_err(client, "100MHz i2c firmware load failed\n"); |
bd985160 HV |
116 | return -ENOSYS; |
117 | } | |
118 | ||
119 | } else { | |
fac9e899 | 120 | v4l_err(client, "firmware load i2c failure\n"); |
bd985160 HV |
121 | return -ENOSYS; |
122 | } | |
123 | ||
124 | } | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | int cx25840_loadfw(struct i2c_client *client) | |
130 | { | |
131 | const struct firmware *fw = NULL; | |
132 | u8 buffer[4], *ptr; | |
133 | int size, send, retval; | |
134 | ||
135 | if (request_firmware(&fw, firmware, FWDEV(client)) != 0) { | |
fac9e899 | 136 | v4l_err(client, "unable to open firmware %s\n", firmware); |
bd985160 HV |
137 | return -EINVAL; |
138 | } | |
139 | ||
140 | start_fw_load(client); | |
141 | ||
142 | buffer[0] = 0x08; | |
143 | buffer[1] = 0x02; | |
144 | buffer[2] = fw->data[0]; | |
145 | buffer[3] = fw->data[1]; | |
146 | retval = fw_write(client, buffer, 4); | |
147 | ||
148 | if (retval < 0) { | |
149 | release_firmware(fw); | |
150 | return retval; | |
151 | } | |
152 | ||
153 | size = fw->size - 2; | |
154 | ptr = fw->data; | |
155 | while (size > 0) { | |
156 | ptr[0] = 0x08; | |
157 | ptr[1] = 0x02; | |
158 | send = size > (FWSEND - 2) ? FWSEND : size + 2; | |
159 | retval = fw_write(client, ptr, send); | |
160 | ||
161 | if (retval < 0) { | |
162 | release_firmware(fw); | |
163 | return retval; | |
164 | } | |
165 | ||
166 | size -= FWSEND - 2; | |
167 | ptr += FWSEND - 2; | |
168 | } | |
169 | ||
170 | end_fw_load(client); | |
171 | ||
172 | size = fw->size; | |
173 | release_firmware(fw); | |
174 | ||
175 | return check_fw_load(client, size); | |
176 | } |