380 lines
9.3 KiB
C
380 lines
9.3 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
|
||
|
* Author: James.Qian.Wang <james.qian.wang@arm.com>
|
||
|
*
|
||
|
*/
|
||
|
#include <linux/of.h>
|
||
|
|
||
|
#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;
|
||
|
unsigned long avail_comps = pipe->avail_comps;
|
||
|
|
||
|
for_each_set_bit(i, &avail_comps, 32) {
|
||
|
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;
|
||
|
unsigned long comp_mask_local = (unsigned long)comp_mask;
|
||
|
int id;
|
||
|
|
||
|
id = find_first_bit(&comp_mask_local, 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;
|
||
|
unsigned long avail_comps = pipe->avail_comps;
|
||
|
|
||
|
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");
|
||
|
|
||
|
for_each_set_bit(id, &avail_comps, 32) {
|
||
|
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;
|
||
|
unsigned long supported_inputs = c->supported_inputs;
|
||
|
|
||
|
for_each_set_bit(id, &supported_inputs, 32) {
|
||
|
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;
|
||
|
unsigned long avail_comps = pipe->avail_comps;
|
||
|
|
||
|
for_each_set_bit(id, &avail_comps, 32) {
|
||
|
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;
|
||
|
unsigned long avail_comps;
|
||
|
|
||
|
seq_printf(sf, "\n======== Pipeline-%d ==========\n", pipe->id);
|
||
|
|
||
|
if (pipe->funcs && pipe->funcs->dump_register)
|
||
|
pipe->funcs->dump_register(pipe, sf);
|
||
|
|
||
|
avail_comps = pipe->avail_comps;
|
||
|
for_each_set_bit(id, &avail_comps, 32) {
|
||
|
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);
|
||
|
}
|
||
|
}
|