| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. |
| * Author: James.Qian.Wang <james.qian.wang@arm.com> |
| * |
| */ |
| #include <drm/drm_print.h> |
| |
| #include "komeda_dev.h" |
| #include "komeda_pipeline.h" |
| |
| /** komeda_pipeline_add - Add a pipeline to &komeda_dev */ |
| struct komeda_pipeline * |
| komeda_pipeline_add(struct komeda_dev *mdev, size_t size, |
| const struct komeda_pipeline_funcs *funcs) |
| { |
| struct komeda_pipeline *pipe; |
| |
| if (mdev->n_pipelines + 1 > KOMEDA_MAX_PIPELINES) { |
| DRM_ERROR("Exceed max support %d pipelines.\n", |
| KOMEDA_MAX_PIPELINES); |
| return ERR_PTR(-ENOSPC); |
| } |
| |
| if (size < sizeof(*pipe)) { |
| DRM_ERROR("Request pipeline size too small.\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| pipe = devm_kzalloc(mdev->dev, size, GFP_KERNEL); |
| if (!pipe) |
| return ERR_PTR(-ENOMEM); |
| |
| pipe->mdev = mdev; |
| pipe->id = mdev->n_pipelines; |
| pipe->funcs = funcs; |
| |
| mdev->pipelines[mdev->n_pipelines] = pipe; |
| mdev->n_pipelines++; |
| |
| return pipe; |
| } |
| |
| void komeda_pipeline_destroy(struct komeda_dev *mdev, |
| struct komeda_pipeline *pipe) |
| { |
| struct komeda_component *c; |
| int i; |
| |
| dp_for_each_set_bit(i, pipe->avail_comps) { |
| c = komeda_pipeline_get_component(pipe, i); |
| komeda_component_destroy(mdev, c); |
| } |
| |
| clk_put(pipe->pxlclk); |
| |
| of_node_put(pipe->of_output_links[0]); |
| of_node_put(pipe->of_output_links[1]); |
| of_node_put(pipe->of_output_port); |
| of_node_put(pipe->of_node); |
| |
| devm_kfree(mdev->dev, pipe); |
| } |
| |
| static struct komeda_component ** |
| komeda_pipeline_get_component_pos(struct komeda_pipeline *pipe, int id) |
| { |
| struct komeda_dev *mdev = pipe->mdev; |
| struct komeda_pipeline *temp = NULL; |
| struct komeda_component **pos = NULL; |
| |
| switch (id) { |
| case KOMEDA_COMPONENT_LAYER0: |
| case KOMEDA_COMPONENT_LAYER1: |
| case KOMEDA_COMPONENT_LAYER2: |
| case KOMEDA_COMPONENT_LAYER3: |
| pos = to_cpos(pipe->layers[id - KOMEDA_COMPONENT_LAYER0]); |
| break; |
| case KOMEDA_COMPONENT_WB_LAYER: |
| pos = to_cpos(pipe->wb_layer); |
| break; |
| case KOMEDA_COMPONENT_COMPIZ0: |
| case KOMEDA_COMPONENT_COMPIZ1: |
| temp = mdev->pipelines[id - KOMEDA_COMPONENT_COMPIZ0]; |
| if (!temp) { |
| DRM_ERROR("compiz-%d doesn't exist.\n", id); |
| return NULL; |
| } |
| pos = to_cpos(temp->compiz); |
| break; |
| case KOMEDA_COMPONENT_SCALER0: |
| case KOMEDA_COMPONENT_SCALER1: |
| pos = to_cpos(pipe->scalers[id - KOMEDA_COMPONENT_SCALER0]); |
| break; |
| case KOMEDA_COMPONENT_SPLITTER: |
| pos = to_cpos(pipe->splitter); |
| break; |
| case KOMEDA_COMPONENT_MERGER: |
| pos = to_cpos(pipe->merger); |
| break; |
| case KOMEDA_COMPONENT_IPS0: |
| case KOMEDA_COMPONENT_IPS1: |
| temp = mdev->pipelines[id - KOMEDA_COMPONENT_IPS0]; |
| if (!temp) { |
| DRM_ERROR("ips-%d doesn't exist.\n", id); |
| return NULL; |
| } |
| pos = to_cpos(temp->improc); |
| break; |
| case KOMEDA_COMPONENT_TIMING_CTRLR: |
| pos = to_cpos(pipe->ctrlr); |
| break; |
| default: |
| pos = NULL; |
| DRM_ERROR("Unknown pipeline resource ID: %d.\n", id); |
| break; |
| } |
| |
| return pos; |
| } |
| |
| struct komeda_component * |
| komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id) |
| { |
| struct komeda_component **pos = NULL; |
| struct komeda_component *c = NULL; |
| |
| pos = komeda_pipeline_get_component_pos(pipe, id); |
| if (pos) |
| c = *pos; |
| |
| return c; |
| } |
| |
| struct komeda_component * |
| komeda_pipeline_get_first_component(struct komeda_pipeline *pipe, |
| u32 comp_mask) |
| { |
| struct komeda_component *c = NULL; |
| int id; |
| |
| id = find_first_bit((unsigned long *)&comp_mask, 32); |
| if (id < 32) |
| c = komeda_pipeline_get_component(pipe, id); |
| |
| return c; |
| } |
| |
| static struct komeda_component * |
| komeda_component_pickup_input(struct komeda_component *c, u32 avail_comps) |
| { |
| u32 avail_inputs = c->supported_inputs & (avail_comps); |
| |
| return komeda_pipeline_get_first_component(c->pipeline, avail_inputs); |
| } |
| |
| /** komeda_component_add - Add a component to &komeda_pipeline */ |
| struct komeda_component * |
| komeda_component_add(struct komeda_pipeline *pipe, |
| size_t comp_sz, u32 id, u32 hw_id, |
| const struct komeda_component_funcs *funcs, |
| u8 max_active_inputs, u32 supported_inputs, |
| u8 max_active_outputs, u32 __iomem *reg, |
| const char *name_fmt, ...) |
| { |
| struct komeda_component **pos; |
| struct komeda_component *c; |
| int idx, *num = NULL; |
| |
| if (max_active_inputs > KOMEDA_COMPONENT_N_INPUTS) { |
| WARN(1, "please large KOMEDA_COMPONENT_N_INPUTS to %d.\n", |
| max_active_inputs); |
| return ERR_PTR(-ENOSPC); |
| } |
| |
| pos = komeda_pipeline_get_component_pos(pipe, id); |
| if (!pos || (*pos)) |
| return ERR_PTR(-EINVAL); |
| |
| if (has_bit(id, KOMEDA_PIPELINE_LAYERS)) { |
| idx = id - KOMEDA_COMPONENT_LAYER0; |
| num = &pipe->n_layers; |
| if (idx != pipe->n_layers) { |
| DRM_ERROR("please add Layer by id sequence.\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| } else if (has_bit(id, KOMEDA_PIPELINE_SCALERS)) { |
| idx = id - KOMEDA_COMPONENT_SCALER0; |
| num = &pipe->n_scalers; |
| if (idx != pipe->n_scalers) { |
| DRM_ERROR("please add Scaler by id sequence.\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| } |
| |
| c = devm_kzalloc(pipe->mdev->dev, comp_sz, GFP_KERNEL); |
| if (!c) |
| return ERR_PTR(-ENOMEM); |
| |
| c->id = id; |
| c->hw_id = hw_id; |
| c->reg = reg; |
| c->pipeline = pipe; |
| c->max_active_inputs = max_active_inputs; |
| c->max_active_outputs = max_active_outputs; |
| c->supported_inputs = supported_inputs; |
| c->funcs = funcs; |
| |
| if (name_fmt) { |
| va_list args; |
| |
| va_start(args, name_fmt); |
| vsnprintf(c->name, sizeof(c->name), name_fmt, args); |
| va_end(args); |
| } |
| |
| if (num) |
| *num = *num + 1; |
| |
| pipe->avail_comps |= BIT(c->id); |
| *pos = c; |
| |
| return c; |
| } |
| |
| void komeda_component_destroy(struct komeda_dev *mdev, |
| struct komeda_component *c) |
| { |
| devm_kfree(mdev->dev, c); |
| } |
| |
| static void komeda_component_dump(struct komeda_component *c) |
| { |
| if (!c) |
| return; |
| |
| DRM_DEBUG(" %s: ID %d-0x%08lx.\n", |
| c->name, c->id, BIT(c->id)); |
| DRM_DEBUG(" max_active_inputs:%d, supported_inputs: 0x%08x.\n", |
| c->max_active_inputs, c->supported_inputs); |
| DRM_DEBUG(" max_active_outputs:%d, supported_outputs: 0x%08x.\n", |
| c->max_active_outputs, c->supported_outputs); |
| } |
| |
| static void komeda_pipeline_dump(struct komeda_pipeline *pipe) |
| { |
| struct komeda_component *c; |
| int id; |
| |
| DRM_INFO("Pipeline-%d: n_layers: %d, n_scalers: %d, output: %s.\n", |
| pipe->id, pipe->n_layers, pipe->n_scalers, |
| pipe->dual_link ? "dual-link" : "single-link"); |
| DRM_INFO(" output_link[0]: %s.\n", |
| pipe->of_output_links[0] ? |
| pipe->of_output_links[0]->full_name : "none"); |
| DRM_INFO(" output_link[1]: %s.\n", |
| pipe->of_output_links[1] ? |
| pipe->of_output_links[1]->full_name : "none"); |
| |
| dp_for_each_set_bit(id, pipe->avail_comps) { |
| c = komeda_pipeline_get_component(pipe, id); |
| |
| komeda_component_dump(c); |
| } |
| } |
| |
| static void komeda_component_verify_inputs(struct komeda_component *c) |
| { |
| struct komeda_pipeline *pipe = c->pipeline; |
| struct komeda_component *input; |
| int id; |
| |
| dp_for_each_set_bit(id, c->supported_inputs) { |
| input = komeda_pipeline_get_component(pipe, id); |
| if (!input) { |
| c->supported_inputs &= ~(BIT(id)); |
| DRM_WARN("Can not find input(ID-%d) for component: %s.\n", |
| id, c->name); |
| continue; |
| } |
| |
| input->supported_outputs |= BIT(c->id); |
| } |
| } |
| |
| static struct komeda_layer * |
| komeda_get_layer_split_right_layer(struct komeda_pipeline *pipe, |
| struct komeda_layer *left) |
| { |
| int index = left->base.id - KOMEDA_COMPONENT_LAYER0; |
| int i; |
| |
| for (i = index + 1; i < pipe->n_layers; i++) |
| if (left->layer_type == pipe->layers[i]->layer_type) |
| return pipe->layers[i]; |
| return NULL; |
| } |
| |
| static void komeda_pipeline_assemble(struct komeda_pipeline *pipe) |
| { |
| struct komeda_component *c; |
| struct komeda_layer *layer; |
| int i, id; |
| |
| dp_for_each_set_bit(id, pipe->avail_comps) { |
| c = komeda_pipeline_get_component(pipe, id); |
| komeda_component_verify_inputs(c); |
| } |
| /* calculate right layer for the layer split */ |
| for (i = 0; i < pipe->n_layers; i++) { |
| layer = pipe->layers[i]; |
| |
| layer->right = komeda_get_layer_split_right_layer(pipe, layer); |
| } |
| |
| if (pipe->dual_link && !pipe->ctrlr->supports_dual_link) { |
| pipe->dual_link = false; |
| DRM_WARN("PIPE-%d doesn't support dual-link, ignore DT dual-link configuration.\n", |
| pipe->id); |
| } |
| } |
| |
| /* if pipeline_A accept another pipeline_B's component as input, treat |
| * pipeline_B as slave of pipeline_A. |
| */ |
| struct komeda_pipeline * |
| komeda_pipeline_get_slave(struct komeda_pipeline *master) |
| { |
| struct komeda_component *slave; |
| |
| slave = komeda_component_pickup_input(&master->compiz->base, |
| KOMEDA_PIPELINE_COMPIZS); |
| |
| return slave ? slave->pipeline : NULL; |
| } |
| |
| int komeda_assemble_pipelines(struct komeda_dev *mdev) |
| { |
| struct komeda_pipeline *pipe; |
| int i; |
| |
| for (i = 0; i < mdev->n_pipelines; i++) { |
| pipe = mdev->pipelines[i]; |
| |
| komeda_pipeline_assemble(pipe); |
| komeda_pipeline_dump(pipe); |
| } |
| |
| return 0; |
| } |
| |
| void komeda_pipeline_dump_register(struct komeda_pipeline *pipe, |
| struct seq_file *sf) |
| { |
| struct komeda_component *c; |
| u32 id; |
| |
| seq_printf(sf, "\n======== Pipeline-%d ==========\n", pipe->id); |
| |
| if (pipe->funcs && pipe->funcs->dump_register) |
| pipe->funcs->dump_register(pipe, sf); |
| |
| dp_for_each_set_bit(id, pipe->avail_comps) { |
| c = komeda_pipeline_get_component(pipe, id); |
| |
| seq_printf(sf, "\n------%s------\n", c->name); |
| if (c->funcs->dump_register) |
| c->funcs->dump_register(c, sf); |
| } |
| } |