1 // SPDX-License-Identifier: GPL-2.0-only OR MIT
2 /* Copyright (c) 2023 Imagination Technologies Ltd. */
4 #include "pvr_device.h"
5 #include "pvr_rogue_fwif_stream.h"
6 #include "pvr_stream.h"
8 #include <linux/align.h>
9 #include <linux/slab.h>
10 #include <linux/types.h>
11 #include <uapi/drm/pvr_drm.h>
13 static __always_inline
bool
14 stream_def_is_supported(struct pvr_device
*pvr_dev
, const struct pvr_stream_def
*stream_def
)
16 if (stream_def
->feature
== PVR_FEATURE_NONE
)
19 if (!(stream_def
->feature
& PVR_FEATURE_NOT
) &&
20 pvr_device_has_feature(pvr_dev
, stream_def
->feature
)) {
24 if ((stream_def
->feature
& PVR_FEATURE_NOT
) &&
25 !pvr_device_has_feature(pvr_dev
, stream_def
->feature
& ~PVR_FEATURE_NOT
)) {
33 pvr_stream_get_data(u8
*stream
, u32
*stream_offset
, u32 stream_size
, u32 data_size
, u32 align_size
,
36 *stream_offset
= ALIGN(*stream_offset
, align_size
);
38 if ((*stream_offset
+ data_size
) > stream_size
)
41 memcpy(dest
, stream
+ *stream_offset
, data_size
);
43 (*stream_offset
) += data_size
;
49 * pvr_stream_process_1() - Process a single stream and fill destination structure
50 * @pvr_dev: Device pointer.
51 * @stream_def: Stream definition.
52 * @nr_entries: Number of entries in &stream_def.
53 * @stream: Pointer to stream.
54 * @stream_offset: Starting offset within stream.
55 * @stream_size: Size of input stream, in bytes.
56 * @dest: Pointer to destination structure.
57 * @dest_size: Size of destination structure.
58 * @stream_offset_out: Pointer to variable to write updated stream offset to. May be NULL.
62 * * -%EINVAL on malformed stream.
65 pvr_stream_process_1(struct pvr_device
*pvr_dev
, const struct pvr_stream_def
*stream_def
,
66 u32 nr_entries
, u8
*stream
, u32 stream_offset
, u32 stream_size
,
67 u8
*dest
, u32 dest_size
, u32
*stream_offset_out
)
72 for (i
= 0; i
< nr_entries
; i
++) {
73 if (stream_def
[i
].offset
>= dest_size
) {
78 if (!stream_def_is_supported(pvr_dev
, &stream_def
[i
]))
81 switch (stream_def
[i
].size
) {
82 case PVR_STREAM_SIZE_8
:
83 err
= pvr_stream_get_data(stream
, &stream_offset
, stream_size
, sizeof(u8
),
84 sizeof(u8
), dest
+ stream_def
[i
].offset
);
89 case PVR_STREAM_SIZE_16
:
90 err
= pvr_stream_get_data(stream
, &stream_offset
, stream_size
, sizeof(u16
),
91 sizeof(u16
), dest
+ stream_def
[i
].offset
);
96 case PVR_STREAM_SIZE_32
:
97 err
= pvr_stream_get_data(stream
, &stream_offset
, stream_size
, sizeof(u32
),
98 sizeof(u32
), dest
+ stream_def
[i
].offset
);
103 case PVR_STREAM_SIZE_64
:
104 err
= pvr_stream_get_data(stream
, &stream_offset
, stream_size
, sizeof(u64
),
105 sizeof(u64
), dest
+ stream_def
[i
].offset
);
110 case PVR_STREAM_SIZE_ARRAY
:
111 err
= pvr_stream_get_data(stream
, &stream_offset
, stream_size
,
112 stream_def
[i
].array_size
, sizeof(u64
),
113 dest
+ stream_def
[i
].offset
);
120 if (stream_offset_out
)
121 *stream_offset_out
= stream_offset
;
127 pvr_stream_process_ext_stream(struct pvr_device
*pvr_dev
,
128 const struct pvr_stream_cmd_defs
*cmd_defs
, void *ext_stream
,
129 u32 stream_offset
, u32 ext_stream_size
, void *dest
)
131 u32 musthave_masks
[PVR_STREAM_EXTHDR_TYPE_MAX
];
136 /* Copy "must have" mask from device. We clear this as we process the stream. */
137 memcpy(musthave_masks
, pvr_dev
->stream_musthave_quirks
[cmd_defs
->type
],
138 sizeof(musthave_masks
));
141 const struct pvr_stream_ext_header
*header
;
145 err
= pvr_stream_get_data(ext_stream
, &stream_offset
, ext_stream_size
, sizeof(u32
),
146 sizeof(ext_header
), &ext_header
);
150 type
= (ext_header
& PVR_STREAM_EXTHDR_TYPE_MASK
) >> PVR_STREAM_EXTHDR_TYPE_SHIFT
;
151 data
= ext_header
& PVR_STREAM_EXTHDR_DATA_MASK
;
153 if (type
>= cmd_defs
->ext_nr_headers
)
156 header
= &cmd_defs
->ext_headers
[type
];
157 if (data
& ~header
->valid_mask
)
160 musthave_masks
[type
] &= ~data
;
162 for (i
= 0; i
< header
->ext_streams_num
; i
++) {
163 const struct pvr_stream_ext_def
*ext_def
= &header
->ext_streams
[i
];
165 if (!(ext_header
& ext_def
->header_mask
))
168 if (!pvr_device_has_uapi_quirk(pvr_dev
, ext_def
->quirk
))
171 err
= pvr_stream_process_1(pvr_dev
, ext_def
->stream
, ext_def
->stream_len
,
172 ext_stream
, stream_offset
,
173 ext_stream_size
, dest
,
174 cmd_defs
->dest_size
, &stream_offset
);
178 } while (ext_header
& PVR_STREAM_EXTHDR_CONTINUATION
);
181 * Verify that "must have" mask is now zero. If it isn't then one of the "must have" quirks
182 * for this command was not present.
184 for (i
= 0; i
< cmd_defs
->ext_nr_headers
; i
++) {
185 if (musthave_masks
[i
])
193 * pvr_stream_process() - Build FW structure from stream
194 * @pvr_dev: Device pointer.
195 * @cmd_defs: Stream definition.
196 * @stream: Pointer to command stream.
197 * @stream_size: Size of command stream, in bytes.
198 * @dest_out: Pointer to destination buffer.
200 * Caller is responsible for freeing the output structure.
204 * * -%ENOMEM on out of memory, or
205 * * -%EINVAL on malformed stream.
208 pvr_stream_process(struct pvr_device
*pvr_dev
, const struct pvr_stream_cmd_defs
*cmd_defs
,
209 void *stream
, u32 stream_size
, void *dest_out
)
211 u32 stream_offset
= 0;
216 if (!stream
|| !stream_size
)
219 err
= pvr_stream_get_data(stream
, &stream_offset
, stream_size
, sizeof(u32
),
220 sizeof(u32
), &main_stream_len
);
225 * u32 after stream length is padding to ensure u64 alignment, but may be used for expansion
226 * in the future. Verify it's zero.
228 err
= pvr_stream_get_data(stream
, &stream_offset
, stream_size
, sizeof(u32
),
229 sizeof(u32
), &padding
);
233 if (main_stream_len
< stream_offset
|| main_stream_len
> stream_size
|| padding
)
236 err
= pvr_stream_process_1(pvr_dev
, cmd_defs
->main_stream
, cmd_defs
->main_stream_len
,
237 stream
, stream_offset
, main_stream_len
, dest_out
,
238 cmd_defs
->dest_size
, &stream_offset
);
242 if (stream_offset
< stream_size
) {
243 err
= pvr_stream_process_ext_stream(pvr_dev
, cmd_defs
, stream
, stream_offset
,
244 stream_size
, dest_out
);
251 * If we don't have an extension stream then there must not be any "must have"
252 * quirks for this command.
254 for (i
= 0; i
< cmd_defs
->ext_nr_headers
; i
++) {
255 if (pvr_dev
->stream_musthave_quirks
[cmd_defs
->type
][i
])
264 * pvr_stream_create_musthave_masks() - Create "must have" masks for streams based on current device
266 * @pvr_dev: Device pointer.
269 pvr_stream_create_musthave_masks(struct pvr_device
*pvr_dev
)
271 memset(pvr_dev
->stream_musthave_quirks
, 0, sizeof(pvr_dev
->stream_musthave_quirks
));
273 if (pvr_device_has_uapi_quirk(pvr_dev
, 47217))
274 pvr_dev
->stream_musthave_quirks
[PVR_STREAM_TYPE_FRAG
][0] |=
275 PVR_STREAM_EXTHDR_FRAG0_BRN47217
;
277 if (pvr_device_has_uapi_quirk(pvr_dev
, 49927)) {
278 pvr_dev
->stream_musthave_quirks
[PVR_STREAM_TYPE_GEOM
][0] |=
279 PVR_STREAM_EXTHDR_GEOM0_BRN49927
;
280 pvr_dev
->stream_musthave_quirks
[PVR_STREAM_TYPE_FRAG
][0] |=
281 PVR_STREAM_EXTHDR_FRAG0_BRN49927
;
282 pvr_dev
->stream_musthave_quirks
[PVR_STREAM_TYPE_COMPUTE
][0] |=
283 PVR_STREAM_EXTHDR_COMPUTE0_BRN49927
;