a9377c735c9cd2d607f0e527026f705c07649188
[linux-2.6-block.git] / drivers / gpu / drm / imagination / pvr_fw.c
1 // SPDX-License-Identifier: GPL-2.0-only OR MIT
2 /* Copyright (c) 2023 Imagination Technologies Ltd. */
3
4 #include "pvr_device.h"
5 #include "pvr_device_info.h"
6 #include "pvr_fw.h"
7
8 #include <drm/drm_drv.h>
9 #include <linux/firmware.h>
10 #include <linux/sizes.h>
11
12 #define FW_MAX_SUPPORTED_MAJOR_VERSION 1
13
14 /**
15  * pvr_fw_validate() - Parse firmware header and check compatibility
16  * @pvr_dev: Device pointer.
17  *
18  * Returns:
19  *  * 0 on success, or
20  *  * -EINVAL if firmware is incompatible.
21  */
22 static int
23 pvr_fw_validate(struct pvr_device *pvr_dev)
24 {
25         struct drm_device *drm_dev = from_pvr_device(pvr_dev);
26         const struct firmware *firmware = pvr_dev->fw_dev.firmware;
27         const struct pvr_fw_layout_entry *layout_entries;
28         const struct pvr_fw_info_header *header;
29         const u8 *fw = firmware->data;
30         u32 fw_offset = firmware->size - SZ_4K;
31         u32 layout_table_size;
32         u32 entry;
33
34         if (firmware->size < SZ_4K || (firmware->size % FW_BLOCK_SIZE))
35                 return -EINVAL;
36
37         header = (const struct pvr_fw_info_header *)&fw[fw_offset];
38
39         if (header->info_version != PVR_FW_INFO_VERSION) {
40                 drm_err(drm_dev, "Unsupported fw info version %u\n",
41                         header->info_version);
42                 return -EINVAL;
43         }
44
45         if (header->header_len != sizeof(struct pvr_fw_info_header) ||
46             header->layout_entry_size != sizeof(struct pvr_fw_layout_entry) ||
47             header->layout_entry_num > PVR_FW_INFO_MAX_NUM_ENTRIES) {
48                 drm_err(drm_dev, "FW info format mismatch\n");
49                 return -EINVAL;
50         }
51
52         if (!(header->flags & PVR_FW_FLAGS_OPEN_SOURCE) ||
53             header->fw_version_major > FW_MAX_SUPPORTED_MAJOR_VERSION ||
54             header->fw_version_major == 0) {
55                 drm_err(drm_dev, "Unsupported FW version %u.%u (build: %u%s)\n",
56                         header->fw_version_major, header->fw_version_minor,
57                         header->fw_version_build,
58                         (header->flags & PVR_FW_FLAGS_OPEN_SOURCE) ? " OS" : "");
59                 return -EINVAL;
60         }
61
62         if (pvr_gpu_id_to_packed_bvnc(&pvr_dev->gpu_id) != header->bvnc) {
63                 struct pvr_gpu_id fw_gpu_id;
64
65                 packed_bvnc_to_pvr_gpu_id(header->bvnc, &fw_gpu_id);
66                 drm_err(drm_dev, "FW built for incorrect GPU ID %i.%i.%i.%i (expected %i.%i.%i.%i)\n",
67                         fw_gpu_id.b, fw_gpu_id.v, fw_gpu_id.n, fw_gpu_id.c,
68                         pvr_dev->gpu_id.b, pvr_dev->gpu_id.v, pvr_dev->gpu_id.n, pvr_dev->gpu_id.c);
69                 return -EINVAL;
70         }
71
72         fw_offset += header->header_len;
73         layout_table_size =
74                 header->layout_entry_size * header->layout_entry_num;
75         if ((fw_offset + layout_table_size) > firmware->size)
76                 return -EINVAL;
77
78         layout_entries = (const struct pvr_fw_layout_entry *)&fw[fw_offset];
79         for (entry = 0; entry < header->layout_entry_num; entry++) {
80                 u32 start_addr = layout_entries[entry].base_addr;
81                 u32 end_addr = start_addr + layout_entries[entry].alloc_size;
82
83                 if (start_addr >= end_addr)
84                         return -EINVAL;
85         }
86
87         fw_offset = (firmware->size - SZ_4K) - header->device_info_size;
88
89         drm_info(drm_dev, "FW version v%u.%u (build %u OS)\n", header->fw_version_major,
90                  header->fw_version_minor, header->fw_version_build);
91
92         pvr_dev->fw_version.major = header->fw_version_major;
93         pvr_dev->fw_version.minor = header->fw_version_minor;
94
95         pvr_dev->fw_dev.header = header;
96         pvr_dev->fw_dev.layout_entries = layout_entries;
97
98         return 0;
99 }
100
101 static int
102 pvr_fw_get_device_info(struct pvr_device *pvr_dev)
103 {
104         const struct firmware *firmware = pvr_dev->fw_dev.firmware;
105         struct pvr_fw_device_info_header *header;
106         const u8 *fw = firmware->data;
107         const u64 *dev_info;
108         u32 fw_offset;
109
110         fw_offset = (firmware->size - SZ_4K) - pvr_dev->fw_dev.header->device_info_size;
111
112         header = (struct pvr_fw_device_info_header *)&fw[fw_offset];
113         dev_info = (u64 *)(header + 1);
114
115         pvr_device_info_set_quirks(pvr_dev, dev_info, header->brn_mask_size);
116         dev_info += header->brn_mask_size;
117
118         pvr_device_info_set_enhancements(pvr_dev, dev_info, header->ern_mask_size);
119         dev_info += header->ern_mask_size;
120
121         return pvr_device_info_set_features(pvr_dev, dev_info, header->feature_mask_size,
122                                             header->feature_param_size);
123 }
124
125 /**
126  * pvr_fw_validate_init_device_info() - Validate firmware and initialise device information
127  * @pvr_dev: Target PowerVR device.
128  *
129  * This function must be called before querying device information.
130  *
131  * Returns:
132  *  * 0 on success, or
133  *  * -%EINVAL if firmware validation fails.
134  */
135 int
136 pvr_fw_validate_init_device_info(struct pvr_device *pvr_dev)
137 {
138         int err;
139
140         err = pvr_fw_validate(pvr_dev);
141         if (err)
142                 return err;
143
144         return pvr_fw_get_device_info(pvr_dev);
145 }