]>
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> | |
bd985160 HV |
20 | #include <linux/firmware.h> |
21 | #include <media/v4l2-common.h> | |
31bc09b5 | 22 | #include <media/cx25840.h> |
bd985160 | 23 | |
31bc09b5 | 24 | #include "cx25840-core.h" |
bd985160 | 25 | |
60f6c464 | 26 | #define FWFILE "v4l-cx25840.fw" |
f234081b | 27 | #define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw" |
149783b5 | 28 | #define FWFILE_CX231XX "v4l-cx231xx-avcore-01.fw" |
4263fa8c MI |
29 | |
30 | /* | |
31 | * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the | |
32 | * size of the firmware chunks sent down the I2C bus to the chip. | |
33 | * Previously this had been set to 1024 but unfortunately some I2C | |
34 | * implementations can't transfer data in such big gulps. | |
35 | * Specifically, the pvrusb2 driver has a hard limit of around 60 | |
36 | * bytes, due to the encapsulation there of I2C traffic into USB | |
37 | * messages. So we have to significantly reduce this parameter. | |
38 | */ | |
39 | #define FWSEND 48 | |
bd985160 | 40 | |
d55c7aec | 41 | #define FWDEV(x) &((x)->dev) |
bd985160 | 42 | |
bd985160 HV |
43 | static char *firmware = FWFILE; |
44 | ||
bd985160 HV |
45 | module_param(firmware, charp, 0444); |
46 | ||
bd985160 HV |
47 | MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]"); |
48 | ||
d92c20e0 | 49 | static void start_fw_load(struct i2c_client *client) |
bd985160 HV |
50 | { |
51 | /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ | |
52 | cx25840_write(client, 0x800, 0x00); | |
53 | cx25840_write(client, 0x801, 0x00); | |
54 | // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 | |
55 | cx25840_write(client, 0x803, 0x0b); | |
56 | /* AUTO_INC_DIS=1 */ | |
57 | cx25840_write(client, 0x000, 0x20); | |
bd985160 HV |
58 | } |
59 | ||
d92c20e0 | 60 | static void end_fw_load(struct i2c_client *client) |
bd985160 | 61 | { |
bd985160 HV |
62 | /* AUTO_INC_DIS=0 */ |
63 | cx25840_write(client, 0x000, 0x00); | |
64 | /* DL_ENABLE=0 */ | |
65 | cx25840_write(client, 0x803, 0x03); | |
66 | } | |
67 | ||
d92c20e0 | 68 | static int check_fw_load(struct i2c_client *client, int size) |
bd985160 HV |
69 | { |
70 | /* DL_ADDR_HB DL_ADDR_LB */ | |
71 | int s = cx25840_read(client, 0x801) << 8; | |
72 | s |= cx25840_read(client, 0x800); | |
73 | ||
74 | if (size != s) { | |
fac9e899 | 75 | v4l_err(client, "firmware %s load failed\n", firmware); |
bd985160 HV |
76 | return -EINVAL; |
77 | } | |
78 | ||
fac9e899 | 79 | v4l_info(client, "loaded %s firmware (%d bytes)\n", firmware, size); |
bd985160 HV |
80 | return 0; |
81 | } | |
82 | ||
9ad46a6a | 83 | static int fw_write(struct i2c_client *client, const u8 *data, int size) |
bd985160 | 84 | { |
520ebe5f | 85 | if (i2c_master_send(client, data, size) < size) { |
7d16eaa3 HV |
86 | v4l_err(client, "firmware load i2c failure\n"); |
87 | return -ENOSYS; | |
bd985160 HV |
88 | } |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
93 | int cx25840_loadfw(struct i2c_client *client) | |
94 | { | |
9357b31c | 95 | struct cx25840_state *state = to_state(i2c_get_clientdata(client)); |
bd985160 | 96 | const struct firmware *fw = NULL; |
9ad46a6a DW |
97 | u8 buffer[FWSEND]; |
98 | const u8 *ptr; | |
520ebe5f | 99 | int size, retval; |
95b14fb2 | 100 | int MAX_BUF_SIZE = FWSEND; |
bd985160 | 101 | |
f234081b ST |
102 | if (state->is_cx23885) |
103 | firmware = FWFILE_CX23885; | |
95b14fb2 MCC |
104 | else if (state->is_cx231xx) |
105 | firmware = FWFILE_CX231XX; | |
149783b5 | 106 | |
95b14fb2 MCC |
107 | if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) { |
108 | v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); | |
109 | MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ | |
110 | } | |
f234081b | 111 | |
bd985160 | 112 | if (request_firmware(&fw, firmware, FWDEV(client)) != 0) { |
fac9e899 | 113 | v4l_err(client, "unable to open firmware %s\n", firmware); |
bd985160 HV |
114 | return -EINVAL; |
115 | } | |
116 | ||
117 | start_fw_load(client); | |
118 | ||
119 | buffer[0] = 0x08; | |
120 | buffer[1] = 0x02; | |
bd985160 | 121 | |
9ad46a6a | 122 | size = fw->size; |
bd985160 HV |
123 | ptr = fw->data; |
124 | while (size > 0) { | |
149783b5 | 125 | int len = min(MAX_BUF_SIZE - 2, size); |
9ad46a6a DW |
126 | |
127 | memcpy(buffer + 2, ptr, len); | |
128 | ||
129 | retval = fw_write(client, buffer, len + 2); | |
bd985160 HV |
130 | |
131 | if (retval < 0) { | |
132 | release_firmware(fw); | |
133 | return retval; | |
134 | } | |
135 | ||
9ad46a6a DW |
136 | size -= len; |
137 | ptr += len; | |
bd985160 HV |
138 | } |
139 | ||
140 | end_fw_load(client); | |
141 | ||
142 | size = fw->size; | |
143 | release_firmware(fw); | |
144 | ||
145 | return check_fw_load(client, size); | |
146 | } |