media: iris: introduce iris core state management with shared queues

Introduce a core state management for iris driver with the necessary
queues needed for the host firmware communication.

There are 3 types of queues:
Command queue - driver to write any command to firmware.
Message queue - firmware to send any response to the driver.
Debug queue - for the firmware to write debug messages.
Initialize and configure the shared queues during probe.

Different states for core:
IRIS_CORE_DEINIT - default state.
IRIS_CORE_INIT   - core state with core initialized. FW loaded and HW
                   brought out of reset, shared queues established
                   between host driver and firmware.
IRIS_CORE_ERROR  - error state.
      -----------
           |
           V
       -----------
       | DEINIT  |
       -----------
           ^
          / \
         /   \
        /     \
       /       \
      v         v
 -----------   ----------.
 |  INIT  |-->|  ERROR  |
 -----------   ----------.

Tested-by: Stefan Schmidt <stefan.schmidt@linaro.org> # x1e80100 (Dell XPS 13 9345)
Reviewed-by: Stefan Schmidt <stefan.schmidt@linaro.org>
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8550-QRD
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8550-HDK
Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
This commit is contained in:
Dikshita Agarwal
2025-02-07 13:24:44 +05:30
committed by Hans Verkuil
parent fa186c97e3
commit d7378f84e9
10 changed files with 445 additions and 1 deletions

View File

@@ -1,5 +1,7 @@
iris-objs += iris_hfi_gen1_command.o \
iris-objs += iris_core.o \
iris_hfi_gen1_command.o \
iris_hfi_gen2_command.o \
iris_hfi_queue.o \
iris_platform_sm8550.o \
iris_probe.o \
iris_vidc.o \

View File

@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "iris_core.h"
#include "iris_state.h"
void iris_core_deinit(struct iris_core *core)
{
mutex_lock(&core->lock);
iris_hfi_queues_deinit(core);
core->state = IRIS_CORE_DEINIT;
mutex_unlock(&core->lock);
}
int iris_core_init(struct iris_core *core)
{
int ret;
mutex_lock(&core->lock);
if (core->state == IRIS_CORE_INIT) {
ret = 0;
goto exit;
} else if (core->state == IRIS_CORE_ERROR) {
ret = -EINVAL;
goto error;
}
core->state = IRIS_CORE_INIT;
ret = iris_hfi_queues_init(core);
if (ret)
goto error;
mutex_unlock(&core->lock);
return 0;
error:
core->state = IRIS_CORE_DEINIT;
exit:
mutex_unlock(&core->lock);
return ret;
}

View File

@@ -9,7 +9,9 @@
#include <linux/types.h>
#include <media/v4l2-device.h>
#include "iris_hfi_queue.h"
#include "iris_platform_common.h"
#include "iris_state.h"
struct icc_info {
const char *name;
@@ -34,6 +36,15 @@ struct icc_info {
* @clk_count: count of iris clocks
* @resets: table of iris reset clocks
* @iris_platform_data: a structure for platform data
* @state: current state of core
* @iface_q_table_daddr: device address for interface queue table memory
* @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory
* @iface_q_table_vaddr: virtual address for interface queue table memory
* @sfr_vaddr: virtual address for SFR (Sub System Failure Reason) register memory
* @command_queue: shared interface queue to send commands to firmware
* @message_queue: shared interface queue to receive responses from firmware
* @debug_queue: shared interface queue to receive debug info from firmware
* @lock: a lock for this strucure
*/
struct iris_core {
@@ -51,6 +62,18 @@ struct iris_core {
u32 clk_count;
struct reset_control_bulk_data *resets;
const struct iris_platform_data *iris_platform_data;
enum iris_core_state state;
dma_addr_t iface_q_table_daddr;
dma_addr_t sfr_daddr;
void *iface_q_table_vaddr;
void *sfr_vaddr;
struct iris_iface_q_info command_queue;
struct iris_iface_q_info message_queue;
struct iris_iface_q_info debug_queue;
struct mutex lock; /* lock for core related operations */
};
int iris_core_init(struct iris_core *core);
void iris_core_deinit(struct iris_core *core);
#endif

View File

@@ -0,0 +1,127 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "iris_core.h"
#include "iris_hfi_queue.h"
static void iris_hfi_queue_set_header(struct iris_core *core, u32 queue_id,
struct iris_iface_q_info *iface_q)
{
iface_q->qhdr->status = 0x1;
iface_q->qhdr->start_addr = iface_q->device_addr;
iface_q->qhdr->header_type = IFACEQ_DFLT_QHDR;
iface_q->qhdr->queue_type = queue_id;
iface_q->qhdr->q_size = IFACEQ_QUEUE_SIZE / sizeof(u32);
iface_q->qhdr->pkt_size = 0; /* variable packet size */
iface_q->qhdr->rx_wm = 0x1;
iface_q->qhdr->tx_wm = 0x1;
iface_q->qhdr->rx_req = 0x1;
iface_q->qhdr->tx_req = 0x0;
iface_q->qhdr->rx_irq_status = 0x0;
iface_q->qhdr->tx_irq_status = 0x0;
iface_q->qhdr->read_idx = 0x0;
iface_q->qhdr->write_idx = 0x0;
/*
* Set receive request to zero on debug queue as there is no
* need of interrupt from video hardware for debug messages
*/
if (queue_id == IFACEQ_DBGQ_ID)
iface_q->qhdr->rx_req = 0;
}
static void
iris_hfi_queue_init(struct iris_core *core, u32 queue_id, struct iris_iface_q_info *iface_q)
{
struct iris_hfi_queue_table_header *q_tbl_hdr = core->iface_q_table_vaddr;
u32 offset = sizeof(*q_tbl_hdr) + (queue_id * IFACEQ_QUEUE_SIZE);
iface_q->device_addr = core->iface_q_table_daddr + offset;
iface_q->kernel_vaddr =
(void *)((char *)core->iface_q_table_vaddr + offset);
iface_q->qhdr = &q_tbl_hdr->q_hdr[queue_id];
iris_hfi_queue_set_header(core, queue_id, iface_q);
}
static void iris_hfi_queue_deinit(struct iris_iface_q_info *iface_q)
{
iface_q->qhdr = NULL;
iface_q->kernel_vaddr = NULL;
iface_q->device_addr = 0;
}
int iris_hfi_queues_init(struct iris_core *core)
{
struct iris_hfi_queue_table_header *q_tbl_hdr;
u32 queue_size;
/* Iris hardware requires 4K queue alignment */
queue_size = ALIGN((sizeof(*q_tbl_hdr) + (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ)), SZ_4K);
core->iface_q_table_vaddr = dma_alloc_attrs(core->dev, queue_size,
&core->iface_q_table_daddr,
GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
if (!core->iface_q_table_vaddr) {
dev_err(core->dev, "queues alloc and map failed\n");
return -ENOMEM;
}
core->sfr_vaddr = dma_alloc_attrs(core->dev, SFR_SIZE,
&core->sfr_daddr,
GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
if (!core->sfr_vaddr) {
dev_err(core->dev, "sfr alloc and map failed\n");
dma_free_attrs(core->dev, sizeof(*q_tbl_hdr), core->iface_q_table_vaddr,
core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE);
return -ENOMEM;
}
iris_hfi_queue_init(core, IFACEQ_CMDQ_ID, &core->command_queue);
iris_hfi_queue_init(core, IFACEQ_MSGQ_ID, &core->message_queue);
iris_hfi_queue_init(core, IFACEQ_DBGQ_ID, &core->debug_queue);
q_tbl_hdr = (struct iris_hfi_queue_table_header *)core->iface_q_table_vaddr;
q_tbl_hdr->version = 0;
q_tbl_hdr->device_addr = (void *)core;
strscpy(q_tbl_hdr->name, "iris-hfi-queues", sizeof(q_tbl_hdr->name));
q_tbl_hdr->size = sizeof(*q_tbl_hdr);
q_tbl_hdr->qhdr0_offset = sizeof(*q_tbl_hdr) -
(IFACEQ_NUMQ * sizeof(struct iris_hfi_queue_header));
q_tbl_hdr->qhdr_size = sizeof(q_tbl_hdr->q_hdr[0]);
q_tbl_hdr->num_q = IFACEQ_NUMQ;
q_tbl_hdr->num_active_q = IFACEQ_NUMQ;
/* Write sfr size in first word to be used by firmware */
*((u32 *)core->sfr_vaddr) = SFR_SIZE;
return 0;
}
void iris_hfi_queues_deinit(struct iris_core *core)
{
u32 queue_size;
if (!core->iface_q_table_vaddr)
return;
iris_hfi_queue_deinit(&core->debug_queue);
iris_hfi_queue_deinit(&core->message_queue);
iris_hfi_queue_deinit(&core->command_queue);
dma_free_attrs(core->dev, SFR_SIZE, core->sfr_vaddr,
core->sfr_daddr, DMA_ATTR_WRITE_COMBINE);
core->sfr_vaddr = NULL;
core->sfr_daddr = 0;
queue_size = ALIGN(sizeof(struct iris_hfi_queue_table_header) +
(IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ), SZ_4K);
dma_free_attrs(core->dev, queue_size, core->iface_q_table_vaddr,
core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE);
core->iface_q_table_vaddr = NULL;
core->iface_q_table_daddr = 0;
}

View File

@@ -0,0 +1,177 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_HFI_QUEUE_H__
#define __IRIS_HFI_QUEUE_H__
struct iris_core;
/*
* Max 64 Buffers ( 32 input buffers and 32 output buffers)
* can be queued by v4l2 framework at any given time.
*/
#define IFACEQ_MAX_BUF_COUNT 64
/*
* Max session supported are 16.
* this value is used to calcualte the size of
* individual shared queue.
*/
#define IFACE_MAX_PARALLEL_SESSIONS 16
#define IFACEQ_DFLT_QHDR 0x0101
#define IFACEQ_MAX_PKT_SIZE 1024 /* Maximum size of a packet in the queue */
/*
* SFR: Subsystem Failure Reason
* when hardware goes into bad state/failure, firmware fills this memory
* and driver will get to know the actual failure reason from this SFR buffer.
*/
#define SFR_SIZE SZ_4K /* Iris hardware requires 4K queue alignment */
#define IFACEQ_QUEUE_SIZE (IFACEQ_MAX_PKT_SIZE * \
IFACEQ_MAX_BUF_COUNT * IFACE_MAX_PARALLEL_SESSIONS)
/*
* Memory layout of the shared queues:
*
* ||=================|| ^ ^ ^
* || || | | |
* || Queue Table || 288 Bytes | |
* || Header || | | |
* || || | | |
* ||-----------------|| V | |
* ||-----------------|| ^ | |
* || || | | |
* || Command Queue || 56 Bytes | |
* || Header || | | |
* || || | | |
* ||-----------------|| V 456 Bytes |
* ||-----------------|| ^ | |
* || || | | |
* || Message Queue || 56 Bytes | |
* || Header || | | |
* || || | | |
* ||-----------------|| V | Buffer size aligned to 4k
* ||-----------------|| ^ | Overall Queue Size = 2,404 KB
* || || | | |
* || Debug Queue || 56 Bytes | |
* || Header || | | |
* || || | | |
* ||=================|| V V |
* ||=================|| ^ |
* || || | |
* || Command || 800 KB |
* || Queue || | |
* || || | |
* ||=================|| V |
* ||=================|| ^ |
* || || | |
* || Message || 800 KB |
* || Queue || | |
* || || | |
* ||=================|| V |
* ||=================|| ^ |
* || || | |
* || Debug || 800 KB |
* || Queue || | |
* || || | |
* ||=================|| V |
* || || |
* ||=================|| V
*/
/*
* Shared queues are used for communication between driver and firmware.
* There are 3 types of queues:
* Command queue - driver to write any command to firmware.
* Message queue - firmware to send any response to driver.
* Debug queue - firmware to write debug message.
*/
/* Host-firmware shared queue ids */
enum iris_iface_queue {
IFACEQ_CMDQ_ID,
IFACEQ_MSGQ_ID,
IFACEQ_DBGQ_ID,
IFACEQ_NUMQ, /* not an index */
};
/**
* struct iris_hfi_queue_header
*
* @status: Queue status, bits (7:0), 0x1 - active, 0x0 - inactive
* @start_addr: Queue start address in non cached memory
* @queue_type: Queue ID
* @header_type: Default queue header
* @q_size: Queue size
* Number of queue packets if pkt_size is non-zero
* Queue size in bytes if pkt_size is zero
* @pkt_size: Size of queue packet entries
* 0x0: variable queue packet size
* non zero: size of queue packet entry, fixed
* @pkt_drop_cnt: Number of packets dropped by sender
* @rx_wm: Receiver watermark, applicable in event driven mode
* @tx_wm: Sender watermark, applicable in event driven mode
* @rx_req: Receiver sets this bit if queue is empty
* @tx_req: Sender sets this bit if queue is full
* @rx_irq_status: Receiver sets this bit and triggers an interrupt to
* the sender after packets are dequeued. Sender clears this bit
* @tx_irq_status: Sender sets this bit and triggers an interrupt to
* the receiver after packets are queued. Receiver clears this bit
* @read_idx: Index till where receiver has consumed the packets from the queue.
* @write_idx: Index till where sender has written the packets into the queue.
*/
struct iris_hfi_queue_header {
u32 status;
u32 start_addr;
u16 queue_type;
u16 header_type;
u32 q_size;
u32 pkt_size;
u32 pkt_drop_cnt;
u32 rx_wm;
u32 tx_wm;
u32 rx_req;
u32 tx_req;
u32 rx_irq_status;
u32 tx_irq_status;
u32 read_idx;
u32 write_idx;
};
/**
* struct iris_hfi_queue_table_header
*
* @version: Queue table version number
* @size: Queue table size from version to last parametr in qhdr entry
* @qhdr0_offset: Offset to the start of first qhdr
* @qhdr_size: Queue header size in bytes
* @num_q: Total number of queues in Queue table
* @num_active_q: Total number of active queues
* @device_addr: Device address of the queue
* @name: Queue name in characters
* @q_hdr: Array of queue headers
*/
struct iris_hfi_queue_table_header {
u32 version;
u32 size;
u32 qhdr0_offset;
u32 qhdr_size;
u32 num_q;
u32 num_active_q;
void *device_addr;
char name[256]; /* NUL-terminated array of characters */
struct iris_hfi_queue_header q_hdr[IFACEQ_NUMQ];
};
struct iris_iface_q_info {
struct iris_hfi_queue_header *qhdr;
dma_addr_t device_addr;
void *kernel_vaddr;
};
int iris_hfi_queues_init(struct iris_core *core);
void iris_hfi_queues_deinit(struct iris_core *core);
#endif

View File

@@ -31,6 +31,7 @@ struct iris_platform_data {
unsigned int clk_tbl_size;
const char * const *clk_rst_tbl;
unsigned int clk_rst_tbl_size;
u64 dma_mask;
};
#endif

View File

@@ -36,4 +36,6 @@ struct iris_platform_data sm8550_data = {
.opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
.clk_tbl = sm8550_clk_table,
.clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
/* Upper bound of DMA address range */
.dma_mask = 0xe0000000 - 1,
};

View File

@@ -168,15 +168,20 @@ static void iris_remove(struct platform_device *pdev)
if (!core)
return;
iris_core_deinit(core);
video_unregister_device(core->vdev_dec);
v4l2_device_unregister(&core->v4l2_dev);
mutex_destroy(&core->lock);
}
static int iris_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iris_core *core;
u64 dma_mask;
int ret;
core = devm_kzalloc(&pdev->dev, sizeof(*core), GFP_KERNEL);
@@ -184,6 +189,9 @@ static int iris_probe(struct platform_device *pdev)
return -ENOMEM;
core->dev = dev;
core->state = IRIS_CORE_DEINIT;
mutex_init(&core->lock);
core->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(core->reg_base))
return PTR_ERR(core->reg_base);
@@ -209,8 +217,19 @@ static int iris_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, core);
dma_mask = core->iris_platform_data->dma_mask;
ret = dma_set_mask_and_coherent(dev, dma_mask);
if (ret)
goto err_vdev_unreg;
dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
dma_set_seg_boundary(&pdev->dev, DMA_BIT_MASK(32));
return 0;
err_vdev_unreg:
video_unregister_device(core->vdev_dec);
err_v4l2_unreg:
v4l2_device_unregister(&core->v4l2_dev);

View File

@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __IRIS_STATE_H__
#define __IRIS_STATE_H__
/**
* enum iris_core_state
*
* @IRIS_CORE_DEINIT: default state.
* @IRIS_CORE_INIT: core state with core initialized. FW loaded and
* HW brought out of reset, shared queues established
* between host driver and firmware.
* @IRIS_CORE_ERROR: error state.
*
* -----------
* |
* V
* -----------
* +--->| DEINIT |<---+
* | ----------- |
* | | |
* | v |
* | ----------- |
* | / \ |
* | / \ |
* | / \ |
* | v v v
* ----------- -----------
* | INIT |--->| ERROR |
* ----------- -----------
*/
enum iris_core_state {
IRIS_CORE_DEINIT,
IRIS_CORE_INIT,
IRIS_CORE_ERROR,
};
#endif

View File

@@ -81,6 +81,12 @@ int iris_open(struct file *filp)
struct iris_inst *inst;
int ret;
ret = iris_core_init(core);
if (ret) {
dev_err(core->dev, "core init failed\n");
return ret;
}
inst = core->iris_platform_data->get_instance();
if (!inst)
return -ENOMEM;