1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2018 Intel Corporation |
3 | |
4 | #include <linux/device.h> |
5 | #include <linux/firmware.h> |
6 | #include <linux/mm.h> |
7 | #include <linux/slab.h> |
8 | |
9 | #include "ipu3-css.h" |
10 | #include "ipu3-css-fw.h" |
11 | #include "ipu3-dmamap.h" |
12 | |
13 | static void imgu_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, |
14 | const char *name) |
15 | { |
16 | unsigned int i; |
17 | |
18 | dev_dbg(dev, "found firmware binary type %i size %i name %s\n" , |
19 | bi->type, bi->blob.size, name); |
20 | if (bi->type != IMGU_FW_ISP_FIRMWARE) |
21 | return; |
22 | |
23 | dev_dbg(dev, " id %i mode %i bds 0x%x veceven %i/%i out_pins %i\n" , |
24 | bi->info.isp.sp.id, bi->info.isp.sp.pipeline.mode, |
25 | bi->info.isp.sp.bds.supported_bds_factors, |
26 | bi->info.isp.sp.enable.vf_veceven, |
27 | bi->info.isp.sp.vf_dec.is_variable, |
28 | bi->info.isp.num_output_pins); |
29 | |
30 | dev_dbg(dev, " input (%i,%i)-(%i,%i) formats %s%s%s\n" , |
31 | bi->info.isp.sp.input.min_width, |
32 | bi->info.isp.sp.input.min_height, |
33 | bi->info.isp.sp.input.max_width, |
34 | bi->info.isp.sp.input.max_height, |
35 | bi->info.isp.sp.enable.input_yuv ? "yuv420 " : "" , |
36 | bi->info.isp.sp.enable.input_feeder || |
37 | bi->info.isp.sp.enable.input_raw ? "raw8 raw10 " : "" , |
38 | bi->info.isp.sp.enable.input_raw ? "raw12" : "" ); |
39 | |
40 | dev_dbg(dev, " internal (%i,%i)\n" , |
41 | bi->info.isp.sp.internal.max_width, |
42 | bi->info.isp.sp.internal.max_height); |
43 | |
44 | dev_dbg(dev, " output (%i,%i)-(%i,%i) formats" , |
45 | bi->info.isp.sp.output.min_width, |
46 | bi->info.isp.sp.output.min_height, |
47 | bi->info.isp.sp.output.max_width, |
48 | bi->info.isp.sp.output.max_height); |
49 | for (i = 0; i < bi->info.isp.num_output_formats; i++) |
50 | dev_dbg(dev, " %i" , bi->info.isp.output_formats[i]); |
51 | dev_dbg(dev, " vf" ); |
52 | for (i = 0; i < bi->info.isp.num_vf_formats; i++) |
53 | dev_dbg(dev, " %i" , bi->info.isp.vf_formats[i]); |
54 | dev_dbg(dev, "\n" ); |
55 | } |
56 | |
57 | unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi) |
58 | { |
59 | unsigned int width = DIV_ROUND_UP(bi->info.isp.sp.internal.max_width, |
60 | IMGU_OBGRID_TILE_SIZE * 2) + 1; |
61 | unsigned int height = DIV_ROUND_UP(bi->info.isp.sp.internal.max_height, |
62 | IMGU_OBGRID_TILE_SIZE * 2) + 1; |
63 | unsigned int obgrid_size; |
64 | |
65 | width = ALIGN(width, IPU3_UAPI_ISP_VEC_ELEMS / 4); |
66 | obgrid_size = PAGE_ALIGN(width * height * |
67 | sizeof(struct ipu3_uapi_obgrid_param)) * |
68 | bi->info.isp.sp.iterator.num_stripes; |
69 | return obgrid_size; |
70 | } |
71 | |
72 | void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe, |
73 | enum imgu_abi_param_class cls, |
74 | enum imgu_abi_memories mem, |
75 | struct imgu_fw_isp_parameter *par, |
76 | size_t par_size, void *binary_params) |
77 | { |
78 | struct imgu_fw_info *bi = |
79 | &css->fwp->binary_header[css->pipes[pipe].bindex]; |
80 | |
81 | if (par->offset + par->size > |
82 | bi->info.isp.sp.mem_initializers.params[cls][mem].size) |
83 | return NULL; |
84 | |
85 | if (par->size != par_size) |
86 | pr_warn("parameter size doesn't match defined size\n" ); |
87 | |
88 | if (par->size < par_size) |
89 | return NULL; |
90 | |
91 | return binary_params + par->offset; |
92 | } |
93 | |
94 | void imgu_css_fw_cleanup(struct imgu_css *css) |
95 | { |
96 | struct imgu_device *imgu = dev_get_drvdata(dev: css->dev); |
97 | |
98 | if (css->binary) { |
99 | unsigned int i; |
100 | |
101 | for (i = 0; i < css->fwp->file_header.binary_nr; i++) |
102 | imgu_dmamap_free(imgu, map: &css->binary[i]); |
103 | kfree(objp: css->binary); |
104 | } |
105 | if (css->fw) |
106 | release_firmware(fw: css->fw); |
107 | |
108 | css->binary = NULL; |
109 | css->fw = NULL; |
110 | } |
111 | |
112 | int imgu_css_fw_init(struct imgu_css *css) |
113 | { |
114 | static const u32 BLOCK_MAX = 65536; |
115 | struct imgu_device *imgu = dev_get_drvdata(dev: css->dev); |
116 | struct device *dev = css->dev; |
117 | unsigned int i, j, binary_nr; |
118 | int r; |
119 | |
120 | r = request_firmware(fw: &css->fw, IMGU_FW_NAME_20161208, device: css->dev); |
121 | if (r == -ENOENT) |
122 | r = request_firmware(fw: &css->fw, IMGU_FW_NAME, device: css->dev); |
123 | if (r) |
124 | return r; |
125 | |
126 | /* Check and display fw header info */ |
127 | |
128 | css->fwp = (struct imgu_fw_header *)css->fw->data; |
129 | if (css->fw->size < struct_size(css->fwp, binary_header, 1) || |
130 | css->fwp->file_header.h_size != sizeof(struct imgu_fw_bi_file_h)) |
131 | goto bad_fw; |
132 | if (struct_size(css->fwp, binary_header, |
133 | css->fwp->file_header.binary_nr) > css->fw->size) |
134 | goto bad_fw; |
135 | |
136 | dev_info(dev, "loaded firmware version %.64s, %u binaries, %zu bytes\n" , |
137 | css->fwp->file_header.version, css->fwp->file_header.binary_nr, |
138 | css->fw->size); |
139 | |
140 | /* Validate and display info on fw binaries */ |
141 | |
142 | binary_nr = css->fwp->file_header.binary_nr; |
143 | |
144 | css->fw_bl = -1; |
145 | css->fw_sp[0] = -1; |
146 | css->fw_sp[1] = -1; |
147 | |
148 | for (i = 0; i < binary_nr; i++) { |
149 | struct imgu_fw_info *bi = &css->fwp->binary_header[i]; |
150 | const char *name = (void *)css->fwp + bi->blob.prog_name_offset; |
151 | size_t len; |
152 | |
153 | if (bi->blob.prog_name_offset >= css->fw->size) |
154 | goto bad_fw; |
155 | len = strnlen(p: name, maxlen: css->fw->size - bi->blob.prog_name_offset); |
156 | if (len + 1 > css->fw->size - bi->blob.prog_name_offset || |
157 | len + 1 >= IMGU_ABI_MAX_BINARY_NAME) |
158 | goto bad_fw; |
159 | |
160 | if (bi->blob.size != bi->blob.text_size + bi->blob.icache_size |
161 | + bi->blob.data_size + bi->blob.padding_size) |
162 | goto bad_fw; |
163 | if (bi->blob.offset + bi->blob.size > css->fw->size) |
164 | goto bad_fw; |
165 | |
166 | if (bi->type == IMGU_FW_BOOTLOADER_FIRMWARE) { |
167 | css->fw_bl = i; |
168 | if (bi->info.bl.sw_state >= css->iomem_length || |
169 | bi->info.bl.num_dma_cmds >= css->iomem_length || |
170 | bi->info.bl.dma_cmd_list >= css->iomem_length) |
171 | goto bad_fw; |
172 | } |
173 | if (bi->type == IMGU_FW_SP_FIRMWARE || |
174 | bi->type == IMGU_FW_SP1_FIRMWARE) { |
175 | css->fw_sp[bi->type == IMGU_FW_SP_FIRMWARE ? 0 : 1] = i; |
176 | if (bi->info.sp.per_frame_data >= css->iomem_length || |
177 | bi->info.sp.init_dmem_data >= css->iomem_length || |
178 | bi->info.sp.host_sp_queue >= css->iomem_length || |
179 | bi->info.sp.isp_started >= css->iomem_length || |
180 | bi->info.sp.sw_state >= css->iomem_length || |
181 | bi->info.sp.sleep_mode >= css->iomem_length || |
182 | bi->info.sp.invalidate_tlb >= css->iomem_length || |
183 | bi->info.sp.host_sp_com >= css->iomem_length || |
184 | bi->info.sp.output + 12 >= css->iomem_length || |
185 | bi->info.sp.host_sp_queues_initialized >= |
186 | css->iomem_length) |
187 | goto bad_fw; |
188 | } |
189 | if (bi->type != IMGU_FW_ISP_FIRMWARE) |
190 | continue; |
191 | |
192 | if (bi->info.isp.sp.pipeline.mode >= IPU3_CSS_PIPE_ID_NUM) |
193 | goto bad_fw; |
194 | |
195 | if (bi->info.isp.sp.iterator.num_stripes > |
196 | IPU3_UAPI_MAX_STRIPES) |
197 | goto bad_fw; |
198 | |
199 | if (bi->info.isp.num_vf_formats > IMGU_ABI_FRAME_FORMAT_NUM || |
200 | bi->info.isp.num_output_formats > IMGU_ABI_FRAME_FORMAT_NUM) |
201 | goto bad_fw; |
202 | |
203 | for (j = 0; j < bi->info.isp.num_output_formats; j++) |
204 | if (bi->info.isp.output_formats[j] >= |
205 | IMGU_ABI_FRAME_FORMAT_NUM) |
206 | goto bad_fw; |
207 | for (j = 0; j < bi->info.isp.num_vf_formats; j++) |
208 | if (bi->info.isp.vf_formats[j] >= |
209 | IMGU_ABI_FRAME_FORMAT_NUM) |
210 | goto bad_fw; |
211 | |
212 | if (bi->info.isp.sp.block.block_width <= 0 || |
213 | bi->info.isp.sp.block.block_width > BLOCK_MAX || |
214 | bi->info.isp.sp.block.output_block_height <= 0 || |
215 | bi->info.isp.sp.block.output_block_height > BLOCK_MAX) |
216 | goto bad_fw; |
217 | |
218 | if (bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_PARAM] |
219 | + sizeof(struct imgu_fw_param_memory_offsets) > |
220 | css->fw->size || |
221 | bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_CONFIG] |
222 | + sizeof(struct imgu_fw_config_memory_offsets) > |
223 | css->fw->size || |
224 | bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_STATE] |
225 | + sizeof(struct imgu_fw_state_memory_offsets) > |
226 | css->fw->size) |
227 | goto bad_fw; |
228 | |
229 | imgu_css_fw_show_binary(dev, bi, name); |
230 | } |
231 | |
232 | if (css->fw_bl == -1 || css->fw_sp[0] == -1 || css->fw_sp[1] == -1) |
233 | goto bad_fw; |
234 | |
235 | /* Allocate and map fw binaries into IMGU */ |
236 | |
237 | css->binary = kcalloc(n: binary_nr, size: sizeof(*css->binary), GFP_KERNEL); |
238 | if (!css->binary) { |
239 | r = -ENOMEM; |
240 | goto error_out; |
241 | } |
242 | |
243 | for (i = 0; i < css->fwp->file_header.binary_nr; i++) { |
244 | struct imgu_fw_info *bi = &css->fwp->binary_header[i]; |
245 | void *blob = (void *)css->fwp + bi->blob.offset; |
246 | size_t size = bi->blob.size; |
247 | |
248 | if (!imgu_dmamap_alloc(imgu, map: &css->binary[i], len: size)) { |
249 | r = -ENOMEM; |
250 | goto error_out; |
251 | } |
252 | memcpy(css->binary[i].vaddr, blob, size); |
253 | } |
254 | |
255 | return 0; |
256 | |
257 | bad_fw: |
258 | dev_err(dev, "invalid firmware binary, size %u\n" , (int)css->fw->size); |
259 | r = -ENODEV; |
260 | |
261 | error_out: |
262 | imgu_css_fw_cleanup(css); |
263 | return r; |
264 | } |
265 | |