188 lines
4.8 KiB
C
Executable File
188 lines
4.8 KiB
C
Executable File
/*
|
|
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com
|
|
*
|
|
* Interface file betwen DECON and Writeback for Samsung EXYNOS DPU driver
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/clk-provider.h>
|
|
#include "decon.h"
|
|
|
|
static irqreturn_t decon_wb_irq_handler(int irq, void *dev_data)
|
|
{
|
|
struct decon_device *decon = dev_data;
|
|
u32 irq_sts_reg, ext_irq;
|
|
ext_irq = 0;
|
|
|
|
spin_lock(&decon->slock);
|
|
if (IS_DECON_OFF_STATE(decon))
|
|
goto irq_end;
|
|
|
|
irq_sts_reg = decon_reg_get_interrupt_and_clear(decon->id, &ext_irq);
|
|
|
|
if (irq_sts_reg & DPU_FRAME_DONE_INT_EN)
|
|
DPU_EVENT_LOG(DPU_EVT_DECON_FRAMEDONE, &decon->sd, ktime_set(0, 0));
|
|
|
|
if (ext_irq & DPU_RESOURCE_CONFLICT_INT_EN) {
|
|
DPU_EVENT_LOG(DPU_EVT_RSC_CONFLICT, &decon->sd, ktime_set(0, 0));
|
|
decon_err("DECON%d Resource Conflict(ext_irq=0x%x, irq_sts=0x%x)\n",
|
|
decon->id, ext_irq, irq_sts_reg);
|
|
}
|
|
irq_end:
|
|
spin_unlock(&decon->slock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
int decon_wb_register_irq(struct decon_device *decon)
|
|
{
|
|
struct device *dev = decon->dev;
|
|
struct platform_device *pdev;
|
|
struct resource *res;
|
|
int ret = 0;
|
|
|
|
pdev = container_of(dev, struct platform_device, dev);
|
|
|
|
/* Get IRQ resource and register IRQ handler. */
|
|
/* 0: Under Flow irq */
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
ret = devm_request_irq(dev, res->start, decon_wb_irq_handler, 0,
|
|
pdev->name, decon);
|
|
if (ret) {
|
|
decon_err("failed to install irq\n");
|
|
return ret;
|
|
}
|
|
|
|
/* 1: FrameStart irq */
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
|
|
ret = devm_request_irq(dev, res->start, decon_wb_irq_handler, 0,
|
|
pdev->name, decon);
|
|
if (ret) {
|
|
decon_err("failed to install irq\n");
|
|
return ret;
|
|
}
|
|
|
|
/* 2: FrameDone irq */
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
|
|
ret = devm_request_irq(dev, res->start, decon_wb_irq_handler, 0,
|
|
pdev->name, decon);
|
|
if (ret) {
|
|
decon_err("failed to install irq\n");
|
|
return ret;
|
|
}
|
|
|
|
/* 3: Extra irq */
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 3);
|
|
ret = devm_request_irq(dev, res->start, decon_wb_irq_handler, 0,
|
|
pdev->name, decon);
|
|
if (ret) {
|
|
decon_err("failed to install irq\n");
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void decon_wb_free_irq(struct decon_device *decon)
|
|
{
|
|
struct device *dev = decon->dev;
|
|
struct platform_device *pdev;
|
|
struct resource *res;
|
|
|
|
pdev = container_of(dev, struct platform_device, dev);
|
|
|
|
/* Unregister IRQ handler. */
|
|
/* 0: Under Flow irq */
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
devm_free_irq(dev, res->start, decon);
|
|
|
|
/* 1: FrameStart irq */
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
|
|
devm_free_irq(dev, res->start, decon);
|
|
|
|
/* 2: FrameDone irq */
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
|
|
devm_free_irq(dev, res->start, decon);
|
|
|
|
/* 3: Extra irq */
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 3);
|
|
devm_free_irq(dev, res->start, decon);
|
|
}
|
|
|
|
int decon_wb_get_clocks(struct decon_device *decon)
|
|
{
|
|
decon->res.aclk = devm_clk_get(decon->dev, "aclk");
|
|
if (IS_ERR_OR_NULL(decon->res.aclk)) {
|
|
decon_err("failed to get aclk\n");
|
|
return PTR_ERR(decon->res.aclk);
|
|
}
|
|
decon->res.busd = devm_clk_get(decon->dev, "busd");
|
|
if (IS_ERR_OR_NULL(decon->res.busd)) {
|
|
decon_err("failed to get decon_busd\n");
|
|
return PTR_ERR(decon->res.busd);
|
|
}
|
|
decon->res.busp = devm_clk_get(decon->dev, "busp");
|
|
if (IS_ERR_OR_NULL(decon->res.busp)) {
|
|
decon_err("failed to get decon_busp\n");
|
|
return PTR_ERR(decon->res.busp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void decon_wb_set_clocks(struct decon_device *decon)
|
|
{
|
|
}
|
|
|
|
static int decon_wb_set_lcd_info(struct decon_device *decon)
|
|
{
|
|
struct decon_lcd *lcd_info;
|
|
|
|
if (decon->lcd_info == NULL) {
|
|
lcd_info = kzalloc(sizeof(struct decon_lcd), GFP_KERNEL);
|
|
if (!lcd_info) {
|
|
decon_err("could not allocate decon_lcd for wb\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
decon->lcd_info = lcd_info;
|
|
}
|
|
|
|
decon->lcd_info->width = 1440;
|
|
decon->lcd_info->height = 2560;
|
|
decon->lcd_info->xres = 1440;
|
|
decon->lcd_info->yres = 2560;
|
|
decon->lcd_info->vfp = 2;
|
|
decon->lcd_info->vbp = 20;
|
|
decon->lcd_info->hfp = 20;
|
|
decon->lcd_info->hbp = 20;
|
|
decon->lcd_info->vsa = 2;
|
|
decon->lcd_info->hsa = 20;
|
|
decon->lcd_info->fps = 60;
|
|
decon->dt.out_type = DECON_OUT_WB;
|
|
decon->dt.psr_mode = DECON_MIPI_COMMAND_MODE;
|
|
decon->dt.trig_mode = DECON_SW_TRIG;
|
|
|
|
decon_info("decon_%d output size for writeback %dx%d\n", decon->id,
|
|
decon->lcd_info->width, decon->lcd_info->height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int decon_wb_get_out_sd(struct decon_device *decon)
|
|
{
|
|
decon->out_sd[0] = decon->dpp_sd[ODMA_WB];
|
|
if (IS_ERR_OR_NULL(decon->out_sd[0])) {
|
|
decon_err("failed to get dpp%d sd\n", ODMA_WB);
|
|
return -ENOMEM;
|
|
}
|
|
decon->out_sd[1] = NULL;
|
|
|
|
decon_wb_set_lcd_info(decon);
|
|
|
|
return 0;
|
|
}
|