| // SPDX-License-Identifier: GPL-2.0 |
| // Copyright (C) 2015 - 2022 Intel Corporation |
| |
| #include <linux/bitmap.h> |
| #include <linux/errno.h> |
| #include <linux/gfp.h> |
| #include <linux/slab.h> |
| #include <linux/device.h> |
| |
| #include <uapi/linux/ipu-psys.h> |
| |
| #include "ipu-fw-psys.h" |
| #include "ipu-psys.h" |
| |
| struct ipu6_psys_hw_res_variant hw_var; |
| void ipu6_psys_hw_res_variant_init(void) |
| { |
| if (ipu_ver == IPU_VER_6SE) { |
| hw_var.queue_num = IPU6SE_FW_PSYS_N_PSYS_CMD_QUEUE_ID; |
| hw_var.cell_num = IPU6SE_FW_PSYS_N_CELL_ID; |
| } else if (ipu_ver == IPU_VER_6) { |
| hw_var.queue_num = IPU6_FW_PSYS_N_PSYS_CMD_QUEUE_ID; |
| hw_var.cell_num = IPU6_FW_PSYS_N_CELL_ID; |
| } else if (ipu_ver == IPU_VER_6EP || ipu_ver == IPU_VER_6EP_MTL) { |
| hw_var.queue_num = IPU6_FW_PSYS_N_PSYS_CMD_QUEUE_ID; |
| hw_var.cell_num = IPU6EP_FW_PSYS_N_CELL_ID; |
| } else { |
| WARN(1, "ipu6 psys res var is not initialised correctly."); |
| } |
| |
| hw_var.set_proc_dev_chn = ipu6_fw_psys_set_proc_dev_chn; |
| hw_var.set_proc_dfm_bitmap = ipu6_fw_psys_set_proc_dfm_bitmap; |
| hw_var.set_proc_ext_mem = ipu6_fw_psys_set_process_ext_mem; |
| hw_var.get_pgm_by_proc = |
| ipu6_fw_psys_get_program_manifest_by_process; |
| } |
| |
| static const struct ipu_fw_resource_definitions *get_res(void) |
| { |
| if (ipu_ver == IPU_VER_6SE) |
| return ipu6se_res_defs; |
| |
| if (ipu_ver == IPU_VER_6EP || ipu_ver == IPU_VER_6EP_MTL) |
| return ipu6ep_res_defs; |
| |
| return ipu6_res_defs; |
| } |
| |
| static int ipu_resource_init(struct ipu_resource *res, u32 id, int elements) |
| { |
| if (elements <= 0) { |
| res->bitmap = NULL; |
| return 0; |
| } |
| |
| res->bitmap = bitmap_zalloc(elements, GFP_KERNEL); |
| if (!res->bitmap) |
| return -ENOMEM; |
| res->elements = elements; |
| res->id = id; |
| return 0; |
| } |
| |
| static unsigned long |
| ipu_resource_alloc_with_pos(struct ipu_resource *res, int n, |
| int pos, |
| struct ipu_resource_alloc *alloc, |
| enum ipu_resource_type type) |
| { |
| unsigned long p; |
| |
| if (n <= 0) { |
| alloc->elements = 0; |
| return 0; |
| } |
| |
| if (!res->bitmap || pos >= res->elements) |
| return (unsigned long)(-ENOSPC); |
| |
| p = bitmap_find_next_zero_area(res->bitmap, res->elements, pos, n, 0); |
| alloc->resource = NULL; |
| |
| if (p != pos) |
| return (unsigned long)(-ENOSPC); |
| bitmap_set(res->bitmap, p, n); |
| alloc->resource = res; |
| alloc->elements = n; |
| alloc->pos = p; |
| alloc->type = type; |
| |
| return pos; |
| } |
| |
| static unsigned long |
| ipu_resource_alloc(struct ipu_resource *res, int n, |
| struct ipu_resource_alloc *alloc, |
| enum ipu_resource_type type) |
| { |
| unsigned long p; |
| |
| if (n <= 0) { |
| alloc->elements = 0; |
| return 0; |
| } |
| |
| if (!res->bitmap) |
| return (unsigned long)(-ENOSPC); |
| |
| p = bitmap_find_next_zero_area(res->bitmap, res->elements, 0, n, 0); |
| alloc->resource = NULL; |
| |
| if (p >= res->elements) |
| return (unsigned long)(-ENOSPC); |
| bitmap_set(res->bitmap, p, n); |
| alloc->resource = res; |
| alloc->elements = n; |
| alloc->pos = p; |
| alloc->type = type; |
| |
| return p; |
| } |
| |
| static void ipu_resource_free(struct ipu_resource_alloc *alloc) |
| { |
| if (alloc->elements <= 0) |
| return; |
| |
| if (alloc->type == IPU_RESOURCE_DFM) |
| *alloc->resource->bitmap &= ~(unsigned long)(alloc->elements); |
| else |
| bitmap_clear(alloc->resource->bitmap, alloc->pos, |
| alloc->elements); |
| alloc->resource = NULL; |
| } |
| |
| static void ipu_resource_cleanup(struct ipu_resource *res) |
| { |
| bitmap_free(res->bitmap); |
| res->bitmap = NULL; |
| } |
| |
| /********** IPU PSYS-specific resource handling **********/ |
| int ipu_psys_resource_pool_init(struct ipu_psys_resource_pool *pool) |
| { |
| int i, j, k, ret; |
| const struct ipu_fw_resource_definitions *res_defs; |
| |
| res_defs = get_res(); |
| |
| spin_lock_init(&pool->queues_lock); |
| pool->cells = 0; |
| |
| for (i = 0; i < res_defs->num_dev_channels; i++) { |
| ret = ipu_resource_init(&pool->dev_channels[i], i, |
| res_defs->dev_channels[i]); |
| if (ret) |
| goto error; |
| } |
| |
| for (j = 0; j < res_defs->num_ext_mem_ids; j++) { |
| ret = ipu_resource_init(&pool->ext_memory[j], j, |
| res_defs->ext_mem_ids[j]); |
| if (ret) |
| goto memory_error; |
| } |
| |
| for (k = 0; k < res_defs->num_dfm_ids; k++) { |
| ret = ipu_resource_init(&pool->dfms[k], k, res_defs->dfms[k]); |
| if (ret) |
| goto dfm_error; |
| } |
| |
| spin_lock(&pool->queues_lock); |
| if (ipu_ver == IPU_VER_6SE) |
| bitmap_zero(pool->cmd_queues, |
| IPU6SE_FW_PSYS_N_PSYS_CMD_QUEUE_ID); |
| else |
| bitmap_zero(pool->cmd_queues, |
| IPU6_FW_PSYS_N_PSYS_CMD_QUEUE_ID); |
| spin_unlock(&pool->queues_lock); |
| |
| return 0; |
| |
| dfm_error: |
| for (k--; k >= 0; k--) |
| ipu_resource_cleanup(&pool->dfms[k]); |
| |
| memory_error: |
| for (j--; j >= 0; j--) |
| ipu_resource_cleanup(&pool->ext_memory[j]); |
| |
| error: |
| for (i--; i >= 0; i--) |
| ipu_resource_cleanup(&pool->dev_channels[i]); |
| return ret; |
| } |
| |
| void ipu_psys_resource_copy(struct ipu_psys_resource_pool *src, |
| struct ipu_psys_resource_pool *dest) |
| { |
| int i; |
| const struct ipu_fw_resource_definitions *res_defs; |
| |
| res_defs = get_res(); |
| |
| dest->cells = src->cells; |
| for (i = 0; i < res_defs->num_dev_channels; i++) |
| *dest->dev_channels[i].bitmap = *src->dev_channels[i].bitmap; |
| |
| for (i = 0; i < res_defs->num_ext_mem_ids; i++) |
| *dest->ext_memory[i].bitmap = *src->ext_memory[i].bitmap; |
| |
| for (i = 0; i < res_defs->num_dfm_ids; i++) |
| *dest->dfms[i].bitmap = *src->dfms[i].bitmap; |
| } |
| |
| void ipu_psys_resource_pool_cleanup(struct ipu_psys_resource_pool |
| *pool) |
| { |
| u32 i; |
| const struct ipu_fw_resource_definitions *res_defs; |
| |
| res_defs = get_res(); |
| for (i = 0; i < res_defs->num_dev_channels; i++) |
| ipu_resource_cleanup(&pool->dev_channels[i]); |
| |
| for (i = 0; i < res_defs->num_ext_mem_ids; i++) |
| ipu_resource_cleanup(&pool->ext_memory[i]); |
| |
| for (i = 0; i < res_defs->num_dfm_ids; i++) |
| ipu_resource_cleanup(&pool->dfms[i]); |
| } |
| |
| static int __alloc_one_resrc(const struct device *dev, |
| struct ipu_fw_psys_process *process, |
| struct ipu_resource *resource, |
| struct ipu_fw_generic_program_manifest *pm, |
| u32 resource_id, |
| struct ipu_psys_resource_alloc *alloc) |
| { |
| const u16 resource_req = pm->dev_chn_size[resource_id]; |
| const u16 resource_offset_req = pm->dev_chn_offset[resource_id]; |
| unsigned long retl; |
| |
| if (!resource_req) |
| return -ENXIO; |
| |
| if (alloc->resources >= IPU_MAX_RESOURCES) { |
| dev_err(dev, "out of resource handles\n"); |
| return -ENOSPC; |
| } |
| if (resource_offset_req != (u16)(-1)) |
| retl = ipu_resource_alloc_with_pos |
| (resource, |
| resource_req, |
| resource_offset_req, |
| &alloc->resource_alloc[alloc->resources], |
| IPU_RESOURCE_DEV_CHN); |
| else |
| retl = ipu_resource_alloc |
| (resource, resource_req, |
| &alloc->resource_alloc[alloc->resources], |
| IPU_RESOURCE_DEV_CHN); |
| if (IS_ERR_VALUE(retl)) { |
| dev_dbg(dev, "out of device channel resources\n"); |
| return (int)retl; |
| } |
| alloc->resources++; |
| |
| return 0; |
| } |
| |
| static int ipu_psys_allocate_one_dfm(const struct device *dev, |
| struct ipu_fw_psys_process *process, |
| struct ipu_resource *resource, |
| struct ipu_fw_generic_program_manifest *pm, |
| u32 resource_id, |
| struct ipu_psys_resource_alloc *alloc) |
| { |
| u32 dfm_bitmap_req = pm->dfm_port_bitmap[resource_id]; |
| u32 active_dfm_bitmap_req = pm->dfm_active_port_bitmap[resource_id]; |
| const u8 is_relocatable = pm->is_dfm_relocatable[resource_id]; |
| struct ipu_resource_alloc *alloc_resource; |
| unsigned long p = 0; |
| |
| if (!dfm_bitmap_req) |
| return -ENXIO; |
| |
| if (alloc->resources >= IPU_MAX_RESOURCES) { |
| dev_err(dev, "out of resource handles\n"); |
| return -ENOSPC; |
| } |
| |
| if (!resource->bitmap) |
| return -ENOSPC; |
| |
| if (!is_relocatable) { |
| if (*resource->bitmap & dfm_bitmap_req) { |
| dev_warn(dev, |
| "out of dfm resources, req 0x%x, get 0x%lx\n", |
| dfm_bitmap_req, *resource->bitmap); |
| return -ENOSPC; |
| } |
| *resource->bitmap |= dfm_bitmap_req; |
| } else { |
| unsigned int n = hweight32(dfm_bitmap_req); |
| |
| p = bitmap_find_next_zero_area(resource->bitmap, |
| resource->elements, 0, n, 0); |
| |
| if (p >= resource->elements) |
| return -ENOSPC; |
| |
| bitmap_set(resource->bitmap, p, n); |
| dfm_bitmap_req = dfm_bitmap_req << p; |
| active_dfm_bitmap_req = active_dfm_bitmap_req << p; |
| } |
| |
| alloc_resource = &alloc->resource_alloc[alloc->resources]; |
| alloc_resource->resource = resource; |
| /* Using elements to indicate the bitmap */ |
| alloc_resource->elements = dfm_bitmap_req; |
| alloc_resource->pos = p; |
| alloc_resource->type = IPU_RESOURCE_DFM; |
| |
| alloc->resources++; |
| |
| return 0; |
| } |
| |
| /* |
| * ext_mem_type_id is a generic type id for memory (like DMEM, VMEM) |
| * ext_mem_bank_id is detailed type id for memory (like DMEM0, DMEM1 etc.) |
| */ |
| static int __alloc_mem_resrc(const struct device *dev, |
| struct ipu_fw_psys_process *process, |
| struct ipu_resource *resource, |
| struct ipu_fw_generic_program_manifest *pm, |
| u32 ext_mem_type_id, u32 ext_mem_bank_id, |
| struct ipu_psys_resource_alloc *alloc) |
| { |
| const u16 memory_resource_req = pm->ext_mem_size[ext_mem_type_id]; |
| const u16 memory_offset_req = pm->ext_mem_offset[ext_mem_type_id]; |
| |
| unsigned long retl; |
| |
| if (!memory_resource_req) |
| return -ENXIO; |
| |
| if (alloc->resources >= IPU_MAX_RESOURCES) { |
| dev_err(dev, "out of resource handles\n"); |
| return -ENOSPC; |
| } |
| if (memory_offset_req != (u16)(-1)) |
| retl = ipu_resource_alloc_with_pos |
| (resource, |
| memory_resource_req, memory_offset_req, |
| &alloc->resource_alloc[alloc->resources], |
| IPU_RESOURCE_EXT_MEM); |
| else |
| retl = ipu_resource_alloc |
| (resource, memory_resource_req, |
| &alloc->resource_alloc[alloc->resources], |
| IPU_RESOURCE_EXT_MEM); |
| if (IS_ERR_VALUE(retl)) { |
| dev_dbg(dev, "out of memory resources\n"); |
| return (int)retl; |
| } |
| |
| alloc->resources++; |
| |
| return 0; |
| } |
| |
| int ipu_psys_allocate_cmd_queue_resource(struct ipu_psys_resource_pool *pool) |
| { |
| unsigned long p; |
| int size, start; |
| |
| size = IPU6_FW_PSYS_N_PSYS_CMD_QUEUE_ID; |
| start = IPU6_FW_PSYS_CMD_QUEUE_PPG0_COMMAND_ID; |
| |
| if (ipu_ver == IPU_VER_6SE) { |
| size = IPU6SE_FW_PSYS_N_PSYS_CMD_QUEUE_ID; |
| start = IPU6SE_FW_PSYS_CMD_QUEUE_PPG0_COMMAND_ID; |
| } |
| |
| spin_lock(&pool->queues_lock); |
| /* find available cmd queue from ppg0_cmd_id */ |
| p = bitmap_find_next_zero_area(pool->cmd_queues, size, start, 1, 0); |
| |
| if (p >= size) { |
| spin_unlock(&pool->queues_lock); |
| return -ENOSPC; |
| } |
| |
| bitmap_set(pool->cmd_queues, p, 1); |
| spin_unlock(&pool->queues_lock); |
| |
| return p; |
| } |
| |
| void ipu_psys_free_cmd_queue_resource(struct ipu_psys_resource_pool *pool, |
| u8 queue_id) |
| { |
| spin_lock(&pool->queues_lock); |
| bitmap_clear(pool->cmd_queues, queue_id, 1); |
| spin_unlock(&pool->queues_lock); |
| } |
| |
| int ipu_psys_try_allocate_resources(struct device *dev, |
| struct ipu_fw_psys_process_group *pg, |
| void *pg_manifest, |
| struct ipu_psys_resource_pool *pool) |
| { |
| u32 id, idx; |
| u32 mem_type_id; |
| int ret, i; |
| u16 *process_offset_table; |
| u8 processes; |
| u32 cells = 0; |
| struct ipu_psys_resource_alloc *alloc; |
| const struct ipu_fw_resource_definitions *res_defs; |
| |
| if (!pg) |
| return -EINVAL; |
| process_offset_table = (u16 *)((u8 *)pg + pg->processes_offset); |
| processes = pg->process_count; |
| |
| alloc = kzalloc(sizeof(*alloc), GFP_KERNEL); |
| if (!alloc) |
| return -ENOMEM; |
| |
| res_defs = get_res(); |
| for (i = 0; i < processes; i++) { |
| u32 cell; |
| struct ipu_fw_psys_process *process = |
| (struct ipu_fw_psys_process *) |
| ((char *)pg + process_offset_table[i]); |
| struct ipu_fw_generic_program_manifest pm; |
| |
| memset(&pm, 0, sizeof(pm)); |
| |
| if (!process) { |
| dev_err(dev, "can not get process\n"); |
| ret = -ENOENT; |
| goto free_out; |
| } |
| |
| ret = ipu_fw_psys_get_program_manifest_by_process |
| (&pm, pg_manifest, process); |
| if (ret < 0) { |
| dev_err(dev, "can not get manifest\n"); |
| goto free_out; |
| } |
| |
| if (pm.cell_id == res_defs->num_cells && |
| pm.cell_type_id == res_defs->num_cells_type) { |
| cell = res_defs->num_cells; |
| } else if ((pm.cell_id != res_defs->num_cells && |
| pm.cell_type_id == res_defs->num_cells_type)) { |
| cell = pm.cell_id; |
| } else { |
| /* Find a free cell of desired type */ |
| u32 type = pm.cell_type_id; |
| |
| for (cell = 0; cell < res_defs->num_cells; cell++) |
| if (res_defs->cells[cell] == type && |
| ((pool->cells | cells) & (1 << cell)) == 0) |
| break; |
| if (cell >= res_defs->num_cells) { |
| dev_dbg(dev, "no free cells of right type\n"); |
| ret = -ENOSPC; |
| goto free_out; |
| } |
| } |
| if (cell < res_defs->num_cells) |
| cells |= 1 << cell; |
| if (pool->cells & cells) { |
| dev_dbg(dev, "out of cell resources\n"); |
| ret = -ENOSPC; |
| goto free_out; |
| } |
| |
| if (pm.dev_chn_size) { |
| for (id = 0; id < res_defs->num_dev_channels; id++) { |
| ret = __alloc_one_resrc(dev, process, |
| &pool->dev_channels[id], |
| &pm, id, alloc); |
| if (ret == -ENXIO) |
| continue; |
| |
| if (ret) |
| goto free_out; |
| } |
| } |
| |
| if (pm.dfm_port_bitmap) { |
| for (id = 0; id < res_defs->num_dfm_ids; id++) { |
| ret = ipu_psys_allocate_one_dfm |
| (dev, process, |
| &pool->dfms[id], &pm, id, alloc); |
| if (ret == -ENXIO) |
| continue; |
| |
| if (ret) |
| goto free_out; |
| } |
| } |
| |
| if (pm.ext_mem_size) { |
| for (mem_type_id = 0; |
| mem_type_id < res_defs->num_ext_mem_types; |
| mem_type_id++) { |
| u32 bank = res_defs->num_ext_mem_ids; |
| |
| if (cell != res_defs->num_cells) { |
| idx = res_defs->cell_mem_row * cell + |
| mem_type_id; |
| bank = res_defs->cell_mem[idx]; |
| } |
| |
| if (bank == res_defs->num_ext_mem_ids) |
| continue; |
| |
| ret = __alloc_mem_resrc(dev, process, |
| &pool->ext_memory[bank], |
| &pm, mem_type_id, bank, |
| alloc); |
| if (ret == -ENXIO) |
| continue; |
| |
| if (ret) |
| goto free_out; |
| } |
| } |
| } |
| |
| pool->cells |= cells; |
| |
| kfree(alloc); |
| return 0; |
| |
| free_out: |
| dev_dbg(dev, "failed to try_allocate resource\n"); |
| kfree(alloc); |
| return ret; |
| } |
| |
| /* |
| * Allocate resources for pg from `pool'. Mark the allocated |
| * resources into `alloc'. Returns 0 on success, -ENOSPC |
| * if there are no enough resources, in which cases resources |
| * are not allocated at all, or some other error on other conditions. |
| */ |
| int ipu_psys_allocate_resources(const struct device *dev, |
| struct ipu_fw_psys_process_group *pg, |
| void *pg_manifest, |
| struct ipu_psys_resource_alloc |
| *alloc, struct ipu_psys_resource_pool |
| *pool) |
| { |
| u32 id; |
| u32 mem_type_id; |
| int ret, i; |
| u16 *process_offset_table; |
| u8 processes; |
| u32 cells = 0; |
| int p, idx; |
| u32 bmp, a_bmp; |
| const struct ipu_fw_resource_definitions *res_defs; |
| |
| if (!pg) |
| return -EINVAL; |
| |
| res_defs = get_res(); |
| process_offset_table = (u16 *)((u8 *)pg + pg->processes_offset); |
| processes = pg->process_count; |
| |
| for (i = 0; i < processes; i++) { |
| u32 cell; |
| struct ipu_fw_psys_process *process = |
| (struct ipu_fw_psys_process *) |
| ((char *)pg + process_offset_table[i]); |
| struct ipu_fw_generic_program_manifest pm; |
| |
| memset(&pm, 0, sizeof(pm)); |
| if (!process) { |
| dev_err(dev, "can not get process\n"); |
| ret = -ENOENT; |
| goto free_out; |
| } |
| |
| ret = ipu_fw_psys_get_program_manifest_by_process |
| (&pm, pg_manifest, process); |
| if (ret < 0) { |
| dev_err(dev, "can not get manifest\n"); |
| goto free_out; |
| } |
| |
| if (pm.cell_id == res_defs->num_cells && |
| pm.cell_type_id == res_defs->num_cells_type) { |
| cell = res_defs->num_cells; |
| } else if ((pm.cell_id != res_defs->num_cells && |
| pm.cell_type_id == res_defs->num_cells_type)) { |
| cell = pm.cell_id; |
| } else { |
| /* Find a free cell of desired type */ |
| u32 type = pm.cell_type_id; |
| |
| for (cell = 0; cell < res_defs->num_cells; cell++) |
| if (res_defs->cells[cell] == type && |
| ((pool->cells | cells) & (1 << cell)) == 0) |
| break; |
| if (cell >= res_defs->num_cells) { |
| dev_dbg(dev, "no free cells of right type\n"); |
| ret = -ENOSPC; |
| goto free_out; |
| } |
| ret = ipu_fw_psys_set_process_cell_id(process, 0, cell); |
| if (ret) |
| goto free_out; |
| } |
| if (cell < res_defs->num_cells) |
| cells |= 1 << cell; |
| if (pool->cells & cells) { |
| dev_dbg(dev, "out of cell resources\n"); |
| ret = -ENOSPC; |
| goto free_out; |
| } |
| |
| if (pm.dev_chn_size) { |
| for (id = 0; id < res_defs->num_dev_channels; id++) { |
| ret = __alloc_one_resrc(dev, process, |
| &pool->dev_channels[id], |
| &pm, id, alloc); |
| if (ret == -ENXIO) |
| continue; |
| |
| if (ret) |
| goto free_out; |
| |
| idx = alloc->resources - 1; |
| p = alloc->resource_alloc[idx].pos; |
| ret = ipu_fw_psys_set_proc_dev_chn(process, id, |
| p); |
| if (ret) |
| goto free_out; |
| } |
| } |
| |
| if (pm.dfm_port_bitmap) { |
| for (id = 0; id < res_defs->num_dfm_ids; id++) { |
| ret = ipu_psys_allocate_one_dfm(dev, process, |
| &pool->dfms[id], |
| &pm, id, alloc); |
| if (ret == -ENXIO) |
| continue; |
| |
| if (ret) |
| goto free_out; |
| |
| idx = alloc->resources - 1; |
| p = alloc->resource_alloc[idx].pos; |
| bmp = pm.dfm_port_bitmap[id]; |
| bmp = bmp << p; |
| a_bmp = pm.dfm_active_port_bitmap[id]; |
| a_bmp = a_bmp << p; |
| ret = ipu_fw_psys_set_proc_dfm_bitmap(process, |
| id, bmp, |
| a_bmp); |
| if (ret) |
| goto free_out; |
| } |
| } |
| |
| if (pm.ext_mem_size) { |
| for (mem_type_id = 0; |
| mem_type_id < res_defs->num_ext_mem_types; |
| mem_type_id++) { |
| u32 bank = res_defs->num_ext_mem_ids; |
| |
| if (cell != res_defs->num_cells) { |
| idx = res_defs->cell_mem_row * cell + |
| mem_type_id; |
| bank = res_defs->cell_mem[idx]; |
| } |
| if (bank == res_defs->num_ext_mem_ids) |
| continue; |
| |
| ret = __alloc_mem_resrc(dev, process, |
| &pool->ext_memory[bank], |
| &pm, mem_type_id, |
| bank, alloc); |
| if (ret == -ENXIO) |
| continue; |
| |
| if (ret) |
| goto free_out; |
| |
| /* no return value check here because fw api |
| * will do some checks, and would return |
| * non-zero except mem_type_id == 0. |
| * This maybe caused by that above flow of |
| * allocating mem_bank_id is improper. |
| */ |
| idx = alloc->resources - 1; |
| p = alloc->resource_alloc[idx].pos; |
| ipu_fw_psys_set_process_ext_mem(process, |
| mem_type_id, |
| bank, p); |
| } |
| } |
| } |
| alloc->cells |= cells; |
| pool->cells |= cells; |
| return 0; |
| |
| free_out: |
| dev_err(dev, "failed to allocate resources, ret %d\n", ret); |
| ipu_psys_reset_process_cell(dev, pg, pg_manifest, i + 1); |
| ipu_psys_free_resources(alloc, pool); |
| return ret; |
| } |
| |
| int ipu_psys_move_resources(const struct device *dev, |
| struct ipu_psys_resource_alloc *alloc, |
| struct ipu_psys_resource_pool |
| *source_pool, struct ipu_psys_resource_pool |
| *target_pool) |
| { |
| int i; |
| |
| if (target_pool->cells & alloc->cells) { |
| dev_dbg(dev, "out of cell resources\n"); |
| return -ENOSPC; |
| } |
| |
| for (i = 0; i < alloc->resources; i++) { |
| unsigned long bitmap = 0; |
| unsigned int id = alloc->resource_alloc[i].resource->id; |
| unsigned long fbit, end; |
| |
| switch (alloc->resource_alloc[i].type) { |
| case IPU_RESOURCE_DEV_CHN: |
| bitmap_set(&bitmap, alloc->resource_alloc[i].pos, |
| alloc->resource_alloc[i].elements); |
| if (*target_pool->dev_channels[id].bitmap & bitmap) |
| return -ENOSPC; |
| break; |
| case IPU_RESOURCE_EXT_MEM: |
| end = alloc->resource_alloc[i].elements + |
| alloc->resource_alloc[i].pos; |
| |
| fbit = find_next_bit(target_pool->ext_memory[id].bitmap, |
| end, alloc->resource_alloc[i].pos); |
| /* if find_next_bit returns "end" it didn't find 1bit */ |
| if (end != fbit) |
| return -ENOSPC; |
| break; |
| case IPU_RESOURCE_DFM: |
| bitmap = alloc->resource_alloc[i].elements; |
| if (*target_pool->dfms[id].bitmap & bitmap) |
| return -ENOSPC; |
| break; |
| default: |
| dev_err(dev, "Illegal resource type\n"); |
| return -EINVAL; |
| } |
| } |
| |
| for (i = 0; i < alloc->resources; i++) { |
| u32 id = alloc->resource_alloc[i].resource->id; |
| |
| switch (alloc->resource_alloc[i].type) { |
| case IPU_RESOURCE_DEV_CHN: |
| bitmap_set(target_pool->dev_channels[id].bitmap, |
| alloc->resource_alloc[i].pos, |
| alloc->resource_alloc[i].elements); |
| ipu_resource_free(&alloc->resource_alloc[i]); |
| alloc->resource_alloc[i].resource = |
| &target_pool->dev_channels[id]; |
| break; |
| case IPU_RESOURCE_EXT_MEM: |
| bitmap_set(target_pool->ext_memory[id].bitmap, |
| alloc->resource_alloc[i].pos, |
| alloc->resource_alloc[i].elements); |
| ipu_resource_free(&alloc->resource_alloc[i]); |
| alloc->resource_alloc[i].resource = |
| &target_pool->ext_memory[id]; |
| break; |
| case IPU_RESOURCE_DFM: |
| *target_pool->dfms[id].bitmap |= |
| alloc->resource_alloc[i].elements; |
| *alloc->resource_alloc[i].resource->bitmap &= |
| ~(alloc->resource_alloc[i].elements); |
| alloc->resource_alloc[i].resource = |
| &target_pool->dfms[id]; |
| break; |
| default: |
| /* |
| * Just keep compiler happy. This case failed already |
| * in above loop. |
| */ |
| break; |
| } |
| } |
| |
| target_pool->cells |= alloc->cells; |
| source_pool->cells &= ~alloc->cells; |
| |
| return 0; |
| } |
| |
| void ipu_psys_reset_process_cell(const struct device *dev, |
| struct ipu_fw_psys_process_group *pg, |
| void *pg_manifest, |
| int process_count) |
| { |
| int i; |
| u16 *process_offset_table; |
| const struct ipu_fw_resource_definitions *res_defs; |
| |
| if (!pg) |
| return; |
| |
| res_defs = get_res(); |
| process_offset_table = (u16 *)((u8 *)pg + pg->processes_offset); |
| for (i = 0; i < process_count; i++) { |
| struct ipu_fw_psys_process *process = |
| (struct ipu_fw_psys_process *) |
| ((char *)pg + process_offset_table[i]); |
| struct ipu_fw_generic_program_manifest pm; |
| int ret; |
| |
| if (!process) |
| break; |
| |
| ret = ipu_fw_psys_get_program_manifest_by_process(&pm, |
| pg_manifest, |
| process); |
| if (ret < 0) { |
| dev_err(dev, "can not get manifest\n"); |
| break; |
| } |
| if ((pm.cell_id != res_defs->num_cells && |
| pm.cell_type_id == res_defs->num_cells_type)) |
| continue; |
| /* no return value check here because if finding free cell |
| * failed, process cell would not set then calling clear_cell |
| * will return non-zero. |
| */ |
| ipu_fw_psys_clear_process_cell(process); |
| } |
| } |
| |
| /* Free resources marked in `alloc' from `resources' */ |
| void ipu_psys_free_resources(struct ipu_psys_resource_alloc |
| *alloc, struct ipu_psys_resource_pool *pool) |
| { |
| unsigned int i; |
| |
| pool->cells &= ~alloc->cells; |
| alloc->cells = 0; |
| for (i = 0; i < alloc->resources; i++) |
| ipu_resource_free(&alloc->resource_alloc[i]); |
| alloc->resources = 0; |
| } |