Skip to content

Commit d2d79d2

Browse files
sarah-walker-imgtecmripard
authored andcommitted
drm/imagination: Implement context creation/destruction ioctls
Implement ioctls for the creation and destruction of contexts. Contexts are used for job submission and each is associated with a particular job type. Changes since v8: - Fixed one error path in pvr_stream_process_1() - Corrected license identifiers Changes since v5: - Fix context release in final error path in pvr_context_create() Changes since v3: - Use drm_dev_{enter,exit} Co-developed-by: Boris Brezillon <[email protected]> Signed-off-by: Boris Brezillon <[email protected]> Signed-off-by: Sarah Walker <[email protected]> Signed-off-by: Donald Robson <[email protected]> Link: https://lore.kernel.org/r/ac474a1f7dda2582d290798e4837140a2989aa2a.1700668843.git.donald.robson@imgtec.com Signed-off-by: Maxime Ripard <[email protected]>
1 parent 6eeddda commit d2d79d2

File tree

11 files changed

+1431
-2
lines changed

11 files changed

+1431
-2
lines changed

drivers/gpu/drm/imagination/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ subdir-ccflags-y := -I$(srctree)/$(src)
55

66
powervr-y := \
77
pvr_ccb.o \
8+
pvr_cccb.o \
9+
pvr_context.o \
810
pvr_device.o \
911
pvr_device_info.o \
1012
pvr_drv.o \
@@ -18,6 +20,8 @@ powervr-y := \
1820
pvr_hwrt.o \
1921
pvr_mmu.o \
2022
pvr_power.o \
23+
pvr_stream.o \
24+
pvr_stream_defs.o \
2125
pvr_vm.o \
2226
pvr_vm_mips.o
2327

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/* Copyright (c) 2023 Imagination Technologies Ltd. */
3+
4+
#include "pvr_ccb.h"
5+
#include "pvr_cccb.h"
6+
#include "pvr_device.h"
7+
#include "pvr_gem.h"
8+
#include "pvr_hwrt.h"
9+
10+
#include <linux/compiler.h>
11+
#include <linux/delay.h>
12+
#include <linux/jiffies.h>
13+
#include <linux/mutex.h>
14+
#include <linux/types.h>
15+
16+
static __always_inline u32
17+
get_ccb_space(u32 w_off, u32 r_off, u32 ccb_size)
18+
{
19+
return (((r_off) - (w_off)) + ((ccb_size) - 1)) & ((ccb_size) - 1);
20+
}
21+
22+
static void
23+
cccb_ctrl_init(void *cpu_ptr, void *priv)
24+
{
25+
struct rogue_fwif_cccb_ctl *ctrl = cpu_ptr;
26+
struct pvr_cccb *pvr_cccb = priv;
27+
28+
WRITE_ONCE(ctrl->write_offset, 0);
29+
WRITE_ONCE(ctrl->read_offset, 0);
30+
WRITE_ONCE(ctrl->dep_offset, 0);
31+
WRITE_ONCE(ctrl->wrap_mask, pvr_cccb->wrap_mask);
32+
}
33+
34+
/**
35+
* pvr_cccb_init() - Initialise a Client CCB
36+
* @pvr_dev: Device pointer.
37+
* @pvr_cccb: Pointer to Client CCB structure to initialise.
38+
* @size_log2: Log2 size of Client CCB in bytes.
39+
* @name: Name of owner of Client CCB. Used for fence context.
40+
*
41+
* Return:
42+
* * Zero on success, or
43+
* * Any error code returned by pvr_fw_object_create_and_map().
44+
*/
45+
int
46+
pvr_cccb_init(struct pvr_device *pvr_dev, struct pvr_cccb *pvr_cccb,
47+
u32 size_log2, const char *name)
48+
{
49+
size_t size = 1 << size_log2;
50+
int err;
51+
52+
pvr_cccb->size = size;
53+
pvr_cccb->write_offset = 0;
54+
pvr_cccb->wrap_mask = size - 1;
55+
56+
/*
57+
* Map CCCB and control structure as uncached, so we don't have to flush
58+
* CPU cache repeatedly when polling for space.
59+
*/
60+
pvr_cccb->ctrl = pvr_fw_object_create_and_map(pvr_dev, sizeof(*pvr_cccb->ctrl),
61+
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
62+
cccb_ctrl_init, pvr_cccb,
63+
&pvr_cccb->ctrl_obj);
64+
if (IS_ERR(pvr_cccb->ctrl))
65+
return PTR_ERR(pvr_cccb->ctrl);
66+
67+
pvr_cccb->cccb = pvr_fw_object_create_and_map(pvr_dev, size,
68+
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
69+
NULL, NULL, &pvr_cccb->cccb_obj);
70+
if (IS_ERR(pvr_cccb->cccb)) {
71+
err = PTR_ERR(pvr_cccb->cccb);
72+
goto err_free_ctrl;
73+
}
74+
75+
pvr_fw_object_get_fw_addr(pvr_cccb->ctrl_obj, &pvr_cccb->ctrl_fw_addr);
76+
pvr_fw_object_get_fw_addr(pvr_cccb->cccb_obj, &pvr_cccb->cccb_fw_addr);
77+
78+
return 0;
79+
80+
err_free_ctrl:
81+
pvr_fw_object_unmap_and_destroy(pvr_cccb->ctrl_obj);
82+
83+
return err;
84+
}
85+
86+
/**
87+
* pvr_cccb_fini() - Release Client CCB structure
88+
* @pvr_cccb: Client CCB to release.
89+
*/
90+
void
91+
pvr_cccb_fini(struct pvr_cccb *pvr_cccb)
92+
{
93+
pvr_fw_object_unmap_and_destroy(pvr_cccb->cccb_obj);
94+
pvr_fw_object_unmap_and_destroy(pvr_cccb->ctrl_obj);
95+
}
96+
97+
/**
98+
* pvr_cccb_cmdseq_fits() - Check if a command sequence fits in the CCCB
99+
* @pvr_cccb: Target Client CCB.
100+
* @size: Size of the command sequence.
101+
*
102+
* Check if a command sequence fits in the CCCB we have at hand.
103+
*
104+
* Return:
105+
* * true if the command sequence fits in the CCCB, or
106+
* * false otherwise.
107+
*/
108+
bool pvr_cccb_cmdseq_fits(struct pvr_cccb *pvr_cccb, size_t size)
109+
{
110+
struct rogue_fwif_cccb_ctl *ctrl = pvr_cccb->ctrl;
111+
u32 read_offset, remaining;
112+
bool fits = false;
113+
114+
read_offset = READ_ONCE(ctrl->read_offset);
115+
remaining = pvr_cccb->size - pvr_cccb->write_offset;
116+
117+
/* Always ensure we have enough room for a padding command at the end of the CCCB.
118+
* If our command sequence does not fit, reserve the remaining space for a padding
119+
* command.
120+
*/
121+
if (size + PADDING_COMMAND_SIZE > remaining)
122+
size += remaining;
123+
124+
if (get_ccb_space(pvr_cccb->write_offset, read_offset, pvr_cccb->size) >= size)
125+
fits = true;
126+
127+
return fits;
128+
}
129+
130+
/**
131+
* pvr_cccb_write_command_with_header() - Write a command + command header to a
132+
* Client CCB
133+
* @pvr_cccb: Target Client CCB.
134+
* @cmd_type: Client CCB command type. Must be one of %ROGUE_FWIF_CCB_CMD_TYPE_*.
135+
* @cmd_size: Size of command in bytes.
136+
* @cmd_data: Pointer to command to write.
137+
* @ext_job_ref: External job reference.
138+
* @int_job_ref: Internal job reference.
139+
*
140+
* Caller must make sure there's enough space in CCCB to queue this command. This
141+
* can be done by calling pvr_cccb_cmdseq_fits().
142+
*
143+
* This function is not protected by any lock. The caller must ensure there's
144+
* no concurrent caller, which should be guaranteed by the drm_sched model (job
145+
* submission is serialized in drm_sched_main()).
146+
*/
147+
void
148+
pvr_cccb_write_command_with_header(struct pvr_cccb *pvr_cccb, u32 cmd_type, u32 cmd_size,
149+
void *cmd_data, u32 ext_job_ref, u32 int_job_ref)
150+
{
151+
u32 sz_with_hdr = pvr_cccb_get_size_of_cmd_with_hdr(cmd_size);
152+
struct rogue_fwif_ccb_cmd_header cmd_header = {
153+
.cmd_type = cmd_type,
154+
.cmd_size = ALIGN(cmd_size, 8),
155+
.ext_job_ref = ext_job_ref,
156+
.int_job_ref = int_job_ref,
157+
};
158+
struct rogue_fwif_cccb_ctl *ctrl = pvr_cccb->ctrl;
159+
u32 remaining = pvr_cccb->size - pvr_cccb->write_offset;
160+
u32 required_size, cccb_space, read_offset;
161+
162+
/*
163+
* Always ensure we have enough room for a padding command at the end of
164+
* the CCCB.
165+
*/
166+
if (remaining < sz_with_hdr + PADDING_COMMAND_SIZE) {
167+
/*
168+
* Command would need to wrap, so we need to pad the remainder
169+
* of the CCCB.
170+
*/
171+
required_size = sz_with_hdr + remaining;
172+
} else {
173+
required_size = sz_with_hdr;
174+
}
175+
176+
read_offset = READ_ONCE(ctrl->read_offset);
177+
cccb_space = get_ccb_space(pvr_cccb->write_offset, read_offset, pvr_cccb->size);
178+
if (WARN_ON(cccb_space < required_size))
179+
return;
180+
181+
if (required_size != sz_with_hdr) {
182+
/* Add padding command */
183+
struct rogue_fwif_ccb_cmd_header pad_cmd = {
184+
.cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_PADDING,
185+
.cmd_size = remaining - sizeof(pad_cmd),
186+
};
187+
188+
memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset], &pad_cmd, sizeof(pad_cmd));
189+
pvr_cccb->write_offset = 0;
190+
}
191+
192+
memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset], &cmd_header, sizeof(cmd_header));
193+
memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset + sizeof(cmd_header)], cmd_data, cmd_size);
194+
pvr_cccb->write_offset += sz_with_hdr;
195+
}
196+
197+
static void fill_cmd_kick_data(struct pvr_cccb *cccb, u32 ctx_fw_addr,
198+
struct pvr_hwrt_data *hwrt,
199+
struct rogue_fwif_kccb_cmd_kick_data *k)
200+
{
201+
k->context_fw_addr = ctx_fw_addr;
202+
k->client_woff_update = cccb->write_offset;
203+
k->client_wrap_mask_update = cccb->wrap_mask;
204+
205+
if (hwrt) {
206+
u32 cleanup_state_offset = offsetof(struct rogue_fwif_hwrtdata, cleanup_state);
207+
208+
pvr_fw_object_get_fw_addr_offset(hwrt->fw_obj, cleanup_state_offset,
209+
&k->cleanup_ctl_fw_addr[k->num_cleanup_ctl++]);
210+
}
211+
}
212+
213+
/**
214+
* pvr_cccb_send_kccb_kick: Send KCCB kick to trigger command processing
215+
* @pvr_dev: Device pointer.
216+
* @pvr_cccb: Pointer to CCCB to process.
217+
* @cctx_fw_addr: FW virtual address for context owning this Client CCB.
218+
* @hwrt: HWRT data set associated with this kick. May be %NULL.
219+
*
220+
* You must call pvr_kccb_reserve_slot() and wait for the returned fence to
221+
* signal (if this function didn't return NULL) before calling
222+
* pvr_cccb_send_kccb_kick().
223+
*/
224+
void
225+
pvr_cccb_send_kccb_kick(struct pvr_device *pvr_dev,
226+
struct pvr_cccb *pvr_cccb, u32 cctx_fw_addr,
227+
struct pvr_hwrt_data *hwrt)
228+
{
229+
struct rogue_fwif_kccb_cmd cmd_kick = {
230+
.cmd_type = ROGUE_FWIF_KCCB_CMD_KICK,
231+
};
232+
233+
fill_cmd_kick_data(pvr_cccb, cctx_fw_addr, hwrt, &cmd_kick.cmd_data.cmd_kick_data);
234+
235+
/* Make sure the writes to the CCCB are flushed before sending the KICK. */
236+
wmb();
237+
238+
pvr_kccb_send_cmd_reserved_powered(pvr_dev, &cmd_kick, NULL);
239+
}
240+
241+
void
242+
pvr_cccb_send_kccb_combined_kick(struct pvr_device *pvr_dev,
243+
struct pvr_cccb *geom_cccb,
244+
struct pvr_cccb *frag_cccb,
245+
u32 geom_ctx_fw_addr,
246+
u32 frag_ctx_fw_addr,
247+
struct pvr_hwrt_data *hwrt,
248+
bool frag_is_pr)
249+
{
250+
struct rogue_fwif_kccb_cmd cmd_kick = {
251+
.cmd_type = ROGUE_FWIF_KCCB_CMD_COMBINED_GEOM_FRAG_KICK,
252+
};
253+
254+
fill_cmd_kick_data(geom_cccb, geom_ctx_fw_addr, hwrt,
255+
&cmd_kick.cmd_data.combined_geom_frag_cmd_kick_data.geom_cmd_kick_data);
256+
257+
/* If this is a partial-render job, we don't attach resources to cleanup-ctl array,
258+
* because the resources are already retained by the geometry job.
259+
*/
260+
fill_cmd_kick_data(frag_cccb, frag_ctx_fw_addr, frag_is_pr ? NULL : hwrt,
261+
&cmd_kick.cmd_data.combined_geom_frag_cmd_kick_data.frag_cmd_kick_data);
262+
263+
/* Make sure the writes to the CCCB are flushed before sending the KICK. */
264+
wmb();
265+
266+
pvr_kccb_send_cmd_reserved_powered(pvr_dev, &cmd_kick, NULL);
267+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
2+
/* Copyright (c) 2023 Imagination Technologies Ltd. */
3+
4+
#ifndef PVR_CCCB_H
5+
#define PVR_CCCB_H
6+
7+
#include "pvr_rogue_fwif.h"
8+
#include "pvr_rogue_fwif_shared.h"
9+
10+
#include <linux/mutex.h>
11+
#include <linux/types.h>
12+
13+
#define PADDING_COMMAND_SIZE sizeof(struct rogue_fwif_ccb_cmd_header)
14+
15+
/* Forward declaration from pvr_device.h. */
16+
struct pvr_device;
17+
18+
/* Forward declaration from pvr_gem.h. */
19+
struct pvr_fw_object;
20+
21+
/* Forward declaration from pvr_hwrt.h. */
22+
struct pvr_hwrt_data;
23+
24+
struct pvr_cccb {
25+
/** @ctrl_obj: FW object representing CCCB control structure. */
26+
struct pvr_fw_object *ctrl_obj;
27+
28+
/** @ccb_obj: FW object representing CCCB. */
29+
struct pvr_fw_object *cccb_obj;
30+
31+
/**
32+
* @ctrl: Kernel mapping of CCCB control structure. @lock must be held
33+
* when accessing.
34+
*/
35+
struct rogue_fwif_cccb_ctl *ctrl;
36+
37+
/** @cccb: Kernel mapping of CCCB. @lock must be held when accessing.*/
38+
u8 *cccb;
39+
40+
/** @ctrl_fw_addr: FW virtual address of CCCB control structure. */
41+
u32 ctrl_fw_addr;
42+
/** @ccb_fw_addr: FW virtual address of CCCB. */
43+
u32 cccb_fw_addr;
44+
45+
/** @size: Size of CCCB in bytes. */
46+
size_t size;
47+
48+
/** @write_offset: CCCB write offset. */
49+
u32 write_offset;
50+
51+
/** @wrap_mask: CCCB wrap mask. */
52+
u32 wrap_mask;
53+
};
54+
55+
int pvr_cccb_init(struct pvr_device *pvr_dev, struct pvr_cccb *cccb,
56+
u32 size_log2, const char *name);
57+
void pvr_cccb_fini(struct pvr_cccb *cccb);
58+
59+
void pvr_cccb_write_command_with_header(struct pvr_cccb *pvr_cccb,
60+
u32 cmd_type, u32 cmd_size, void *cmd_data,
61+
u32 ext_job_ref, u32 int_job_ref);
62+
void pvr_cccb_send_kccb_kick(struct pvr_device *pvr_dev,
63+
struct pvr_cccb *pvr_cccb, u32 cctx_fw_addr,
64+
struct pvr_hwrt_data *hwrt);
65+
void pvr_cccb_send_kccb_combined_kick(struct pvr_device *pvr_dev,
66+
struct pvr_cccb *geom_cccb,
67+
struct pvr_cccb *frag_cccb,
68+
u32 geom_ctx_fw_addr,
69+
u32 frag_ctx_fw_addr,
70+
struct pvr_hwrt_data *hwrt,
71+
bool frag_is_pr);
72+
bool pvr_cccb_cmdseq_fits(struct pvr_cccb *pvr_cccb, size_t size);
73+
74+
/**
75+
* pvr_cccb_get_size_of_cmd_with_hdr() - Get the size of a command and its header.
76+
* @cmd_size: Command size.
77+
*
78+
* Returns the size of the command and its header.
79+
*/
80+
static __always_inline u32
81+
pvr_cccb_get_size_of_cmd_with_hdr(u32 cmd_size)
82+
{
83+
WARN_ON(!IS_ALIGNED(cmd_size, 8));
84+
return sizeof(struct rogue_fwif_ccb_cmd_header) + ALIGN(cmd_size, 8);
85+
}
86+
87+
/**
88+
* pvr_cccb_cmdseq_can_fit() - Check if a command sequence can fit in the CCCB.
89+
* @size: Command sequence size.
90+
*
91+
* Returns:
92+
* * true it the CCCB is big enough to contain a command sequence, or
93+
* * false otherwise.
94+
*/
95+
static __always_inline bool
96+
pvr_cccb_cmdseq_can_fit(struct pvr_cccb *pvr_cccb, size_t size)
97+
{
98+
/* We divide the capacity by two to simplify our CCCB fencing logic:
99+
* we want to be sure that, no matter what we had queued before, we
100+
* are able to either queue our command sequence at the end or add a
101+
* padding command and queue the command sequence at the beginning
102+
* of the CCCB. If the command sequence size is bigger than half the
103+
* CCCB capacity, we'd have to queue the padding command and make sure
104+
* the FW is done processing it before queueing our command sequence.
105+
*/
106+
return size + PADDING_COMMAND_SIZE <= pvr_cccb->size / 2;
107+
}
108+
109+
#endif /* PVR_CCCB_H */

0 commit comments

Comments
 (0)