diff options
Diffstat (limited to 'drivers/media/video/cx25840/cx25840-firmware.c')
-rw-r--r-- | drivers/media/video/cx25840/cx25840-firmware.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c new file mode 100644 index 000000000000..df9d50a75542 --- /dev/null +++ b/drivers/media/video/cx25840/cx25840-firmware.c | |||
@@ -0,0 +1,167 @@ | |||
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 | |||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/i2c-algo-bit.h> | ||
22 | #include <linux/firmware.h> | ||
23 | #include <media/v4l2-common.h> | ||
24 | |||
25 | #include "cx25840.h" | ||
26 | |||
27 | #define FWFILE "v4l-cx25840.fw" | ||
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 | |||
41 | static inline void set_i2c_delay(struct i2c_client *client, int delay) | ||
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 | |||
55 | static inline void start_fw_load(struct i2c_client *client) | ||
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 | |||
69 | static inline void end_fw_load(struct i2c_client *client) | ||
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 | |||
80 | static inline int check_fw_load(struct i2c_client *client, int size) | ||
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) { | ||
87 | cx25840_err("firmware %s load failed\n", firmware); | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | |||
91 | cx25840_info("loaded %s firmware (%d bytes)\n", firmware, size); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static inline int fw_write(struct i2c_client *client, u8 * data, int size) | ||
96 | { | ||
97 | if (i2c_master_send(client, data, size) < size) { | ||
98 | |||
99 | if (fastfw) { | ||
100 | cx25840_err("333MHz i2c firmware load failed\n"); | ||
101 | fastfw = 0; | ||
102 | set_i2c_delay(client, 10); | ||
103 | |||
104 | if (i2c_master_send(client, data, size) < size) { | ||
105 | cx25840_err | ||
106 | ("100MHz i2c firmware load failed\n"); | ||
107 | return -ENOSYS; | ||
108 | } | ||
109 | |||
110 | } else { | ||
111 | cx25840_err("firmware load i2c failure\n"); | ||
112 | return -ENOSYS; | ||
113 | } | ||
114 | |||
115 | } | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | int cx25840_loadfw(struct i2c_client *client) | ||
121 | { | ||
122 | const struct firmware *fw = NULL; | ||
123 | u8 buffer[4], *ptr; | ||
124 | int size, send, retval; | ||
125 | |||
126 | if (request_firmware(&fw, firmware, FWDEV(client)) != 0) { | ||
127 | cx25840_err("unable to open firmware %s\n", firmware); | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | start_fw_load(client); | ||
132 | |||
133 | buffer[0] = 0x08; | ||
134 | buffer[1] = 0x02; | ||
135 | buffer[2] = fw->data[0]; | ||
136 | buffer[3] = fw->data[1]; | ||
137 | retval = fw_write(client, buffer, 4); | ||
138 | |||
139 | if (retval < 0) { | ||
140 | release_firmware(fw); | ||
141 | return retval; | ||
142 | } | ||
143 | |||
144 | size = fw->size - 2; | ||
145 | ptr = fw->data; | ||
146 | while (size > 0) { | ||
147 | ptr[0] = 0x08; | ||
148 | ptr[1] = 0x02; | ||
149 | send = size > (FWSEND - 2) ? FWSEND : size + 2; | ||
150 | retval = fw_write(client, ptr, send); | ||
151 | |||
152 | if (retval < 0) { | ||
153 | release_firmware(fw); | ||
154 | return retval; | ||
155 | } | ||
156 | |||
157 | size -= FWSEND - 2; | ||
158 | ptr += FWSEND - 2; | ||
159 | } | ||
160 | |||
161 | end_fw_load(client); | ||
162 | |||
163 | size = fw->size; | ||
164 | release_firmware(fw); | ||
165 | |||
166 | return check_fw_load(client, size); | ||
167 | } | ||