gdc: add gdc driver

PD#165090: gdc add platform drivers base on the arm release.
TODO use v4l2 m2m device driver rewrite.

Change-Id: I1943f762041d005c17abd9b803b69ef68b08d290
Signed-off-by: Jiyu Yang <Jiyu.Yang@amlogic.com>
This commit is contained in:
Jiyu Yang
2018-06-06 21:20:34 +08:00
committed by Yixun Lan
parent 616a5ab789
commit d70c59eeb6
16 changed files with 2959 additions and 0 deletions

View File

@@ -14398,6 +14398,10 @@ AMLOGIC G12A Media codec io bus
M: Nanxin Qin <nanxin.qin@amlogic.com>
F: include/linux/amlogic/media/registers/regs/efuse_regs.h
AMLOGIC Geometric Distorition Correction
M: Jiyu Yang <Jiyu.Yang@amlogic.com>
F: drivers/amlogic/media/gdc/*
AMLOGIC G12A CVBS DRIVER
M: Nian Jing <nian.jing@amlogic.com>
F: drivers/amlogic/media/vout/cvbs/

View File

@@ -1272,6 +1272,21 @@
};
};
gdc:gdc {
#address-cells=<2>;
#size-cells=<2>;
status = "ok";
compatible = "amlogic, g12b-gdc";
reg = <0 0xFF950000 0 0x0000100
0 0xFF63C16C 0 0x0000004
0 0xFF63C100 0 0x0000004>;
interrupts = <0 144 1>;
interrupt-names = "GDC";
clocks = <&clkc CLKID_GDC_CORE_CLK_COMP
&clkc CLKID_GDC_AXI_CLK_COMP >;
clock-names = "core","axi";
};
mesonstream {
compatible = "amlogic, codec, streambuf";
dev_name = "mesonstream";

View File

@@ -316,6 +316,7 @@ CONFIG_AMLOGIC_PIC_DEC=y
CONFIG_AMLOGIC_MEDIA_ENHANCEMENT=y
CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM=y
CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION=y
CONFIG_AMLOGIC_MEDIA_GDC=y
CONFIG_AMLOGIC_DTV_DEMOD=y
CONFIG_AMLOGIC_MMC=y
CONFIG_AMLOGIC_NAND=y

View File

@@ -61,6 +61,7 @@ source "drivers/amlogic/media/deinterlace/Kconfig"
source "drivers/amlogic/media/vin/Kconfig"
source "drivers/amlogic/media/video_processor/Kconfig"
source "drivers/amlogic/media/enhancement/Kconfig"
source "drivers/amlogic/media/gdc/Kconfig"
endif
source "drivers/amlogic/media/dtv_demod/Kconfig"
endmenu

View File

@@ -9,3 +9,4 @@ obj-$(CONFIG_AMLOGIC_MEDIA_VIN) += vin/
obj-$(CONFIG_AMLOGIC_MEDIA_VIDEO_PROCESSOR) += video_processor/
obj-$(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT) += enhancement/
obj-$(CONFIG_AMLOGIC_DTV_DEMOD) += dtv_demod/
obj-$(CONFIG_AMLOGIC_MEDIA_GDC) += gdc/

View File

@@ -0,0 +1,8 @@
#
# gdc driver configuration
#
config AMLOGIC_MEDIA_GDC
bool "Amlogic gdc driver"
default n
help
Select to enable gdc driver.

View File

@@ -0,0 +1,18 @@
FW_SRC := src/fw_lib/acamera_gdc.c \
src/platform/system_gdc_io.c \
src/platform/system_log.c \
app/gdc_main.c \
app/gdc_module.c
FW_SRC_OBJ := $(FW_SRC:.c=.o)
obj-$(CONFIG_AMLOGIC_MEDIA_GDC) += $(FW_SRC_OBJ)
PWD=$(src)
ccflags-y +=-I$(PWD)/app -I$(PWD)/inc
ccflags-y +=-I$(PWD)/inc/api -I$(PWD)/inc/gdc -I$(PWD)/inc/sys
ccflags-y +=-I$(PWD)/src/platform -I$(PWD)/src/fw_lib
ccflags-y += -Idrivers/amlogic/media/common/ion_dev/
ccflags-y += -Idrivers/staging/android/
ccflags-y += -Wno-declaration-after-statement

View File

@@ -0,0 +1,63 @@
/*
* drivers/amlogic/media/gdc/app/gdc_main.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "system_log.h"
#include <linux/interrupt.h>
#include <linux/kernel.h>
//gdc api functions
#include "gdc_api.h"
irqreturn_t interrupt_handler_next(int irq, void *param)
{
//handle the start of frame with gdc_process
struct gdc_settings *gdc_settings = (struct gdc_settings *)param;
gdc_get_frame(gdc_settings);
return IRQ_HANDLED;
}
int gdc_run(struct gdc_settings *g)
{
gdc_stop(g);
LOG(LOG_INFO, "Done gdc load..\n");
//initialise the gdc by the first configuration
if (gdc_init(g) != 0) {
LOG(LOG_ERR, "Failed to initialise GDC block");
return -1;
}
LOG(LOG_INFO, "Done gdc config..\n");
//start gdc process with input address for y and uv planes
if (g->gdc_config.format == NV12) {
gdc_process(g,
(uint32_t)g->y_base_addr,
(uint32_t)g->uv_base_addr);
} else {
gdc_process_yuv420p(g, (uint32_t)g->y_base_addr,
(uint32_t)g->u_base_addr,
(uint32_t)g->v_base_addr);
}
LOG(LOG_DEBUG, "call gdc process\n");
return 0;
}

View File

@@ -0,0 +1,318 @@
/*
* drivers/amlogic/media/gdc/app/gdc_module.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/uio_driver.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/uaccess.h>
#include <meson_ion.h>
#include <linux/delay.h>
#include <linux/of_address.h>
#include <api/gdc_api.h>
#include "system_log.h"
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
//gdc configuration sequence
#include "gdc_config.h"
struct meson_gdc_dev_t *g_gdc_dev;
static const struct of_device_id gdc_dt_match[] = {
{.compatible = "amlogic, g12b-gdc"},
{} };
MODULE_DEVICE_TABLE(of, gdc_dt_match);
//////
static int meson_gdc_open(struct inode *inode, struct file *file)
{
struct meson_gdc_dev_t *gdc_dev = g_gdc_dev;
struct platform_device *pdev = gdc_dev->pdev;
struct mgdc_fh_s *fh = NULL;
char ion_client_name[32];
int rc = 0;
fh = devm_kzalloc(&pdev->dev, sizeof(*fh), GFP_KERNEL);
if (fh == NULL) {
LOG(LOG_DEBUG, "devm alloc failed\n");
return -ENOMEM;
}
get_task_comm(fh->task_comm, current);
LOG(LOG_DEBUG, "%s, %d, call from %s\n",
__func__, __LINE__, fh->task_comm);
file->private_data = fh;
snprintf(ion_client_name, sizeof(fh->task_comm),
"gdc-%s", fh->task_comm);
if (!fh->ion_client)
fh->ion_client = meson_ion_client_create(-1, ion_client_name);
fh->gdev = gdc_dev;
init_waitqueue_head(&fh->irq_queue);
rc = devm_request_irq(&pdev->dev, gdc_dev->irq, interrupt_handler_next,
IRQF_SHARED, "gdc", &fh->gs);
if (rc)
LOG(LOG_DEBUG, "cannot create irq func gdc\n");
return rc;
}
static int meson_gdc_release(struct inode *inode, struct file *file)
{
struct mgdc_fh_s *fh = file->private_data;
struct meson_gdc_dev_t *gdc_dev = fh->gdev;
struct platform_device *pdev = gdc_dev->pdev;
LOG(LOG_DEBUG, "%s, %d\n", __func__, __LINE__);
if (fh->ion_client) {
ion_client_destroy(fh->ion_client);
fh->ion_client = NULL;
}
devm_free_irq(&pdev->dev, gdc_dev->irq, &fh->gs);
return 0;
}
static long meson_gdc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
size_t len;
struct mgdc_fh_s *fh = file->private_data;
struct gdc_settings *gs = &fh->gs;
struct gdc_config *gc = &gs->gdc_config;
long ret = 0;
ion_phys_addr_t addr;
void __user *argp = (void __user *)arg;
gs->fh = fh;
switch (cmd) {
case GDC_PROCESS:
ret = copy_from_user(gs, argp, sizeof(*gs));
if (ret < 0)
LOG(LOG_DEBUG, "copy from user failed\n");
LOG(LOG_DEBUG, "sizeof(gs)=%ld, magic=%d\n",
sizeof(*gs), gs->magic);
//configure gdc config, buffer address and resolution
ret = meson_ion_share_fd_to_phys(fh->ion_client,
gs->out_fd, &addr, &len);
gs->buffer_addr = addr;
gs->buffer_size = len;
if (ret < 0)
LOG(LOG_DEBUG, "import out fd %d failed\n", gs->out_fd);
gs->base_gdc = 0;
gs->current_addr = gs->buffer_addr;
ret = meson_ion_share_fd_to_phys(fh->ion_client,
gc->config_addr, &addr, &len);
gc->config_addr = addr;
ret = meson_ion_share_fd_to_phys(fh->ion_client,
gs->in_fd, &addr, &len);
if (gc->format == NV12) {
gs->y_base_addr = addr;
gs->uv_base_addr = addr +
gc->input_y_stride * gc->input_height;
} else if (gc->format == YV12) {
gs->y_base_addr = addr;
gs->u_base_addr = addr
+ gc->input_y_stride * gc->input_height;
gs->v_base_addr = gs->u_base_addr +
gc->input_c_stride * gc->input_height / 2;
}
if (ret < 0)
LOG(LOG_DEBUG, "import in fd %d failed\n", gs->in_fd);
gs->fh = fh;
ret = gdc_run(gs);
if (ret < 0)
LOG(LOG_DEBUG, "gdc process ret = %ld\n", ret);
ret = wait_event_interruptible_timeout(fh->irq_queue,
(gdc_busy_read() == 0),
msecs_to_jiffies(30));
break;
default:
pr_info("unsupported cmd 0x%x\n", cmd);
break;
}
return ret;
}
static const struct file_operations meson_gdc_fops = {
.owner = THIS_MODULE,
.open = meson_gdc_open,
.release = meson_gdc_release,
.unlocked_ioctl = meson_gdc_ioctl,
.compat_ioctl = meson_gdc_ioctl,
};
static struct miscdevice meson_gdc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "gdc",
.fops = &meson_gdc_fops,
};
static ssize_t gdc_reg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
int i;
len += sprintf(buf+len, "gdc adapter register below\n");
for (i = 0; i <= 0xff; i += 4) {
len += sprintf(buf+len, "\t[0xff950000 + 0x%08x, 0x%-8x\n",
i, system_gdc_read_32(i));
}
return len;
}
static ssize_t gdc_reg_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
LOG(LOG_DEBUG, "%s, %d\n", __func__, __LINE__);
return len;
}
static DEVICE_ATTR(gdc_reg, 0554, gdc_reg_show, gdc_reg_store);
static ssize_t firmware1_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
LOG(LOG_DEBUG, "%s, %d\n", __func__, __LINE__);
return 1;
}
static ssize_t firmware1_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
LOG(LOG_DEBUG, "%s, %d\n", __func__, __LINE__);
//gdc_fw_init();
return 1;
}
static DEVICE_ATTR(firmware1, 0664, firmware1_show, firmware1_store);
static int gdc_platform_probe(struct platform_device *pdev)
{
struct resource *gdc_res;
struct meson_gdc_dev_t *gdc_dev = NULL;
void *clk_cntl = NULL;
void *pd_cntl = NULL;
uint32_t reg_value = 0;
// Initialize irq
gdc_res = platform_get_resource(pdev,
IORESOURCE_MEM, 0);
if (!gdc_res) {
LOG(LOG_ERR, "Error, no IORESOURCE_MEM DT!\n");
return -ENOMEM;
}
if (init_gdc_io(pdev->dev.of_node) != 0) {
LOG(LOG_ERR, "Error on mapping gdc memory!\n");
return -ENOMEM;
}
device_create_file(&pdev->dev, &dev_attr_gdc_reg);
device_create_file(&pdev->dev, &dev_attr_firmware1);
gdc_dev = devm_kzalloc(&pdev->dev, sizeof(*gdc_dev),
GFP_KERNEL);
if (gdc_dev == NULL) {
LOG(LOG_DEBUG, "devm alloc gdc dev failed\n");
return -ENOMEM;
}
gdc_dev->pdev = pdev;
spin_lock_init(&gdc_dev->slock);
gdc_dev->irq = platform_get_irq(pdev, 0);
if (gdc_dev->irq < 0) {
LOG(LOG_DEBUG, "cannot find irq for gdc\n");
return -EINVAL;
}
#if 0
gdc_dev->clk_core = devm_clk_get(&pdev->dev, "core");
rc = clk_set_rate(gdc_dev->clk_core, 800000000);
gdc_dev->clk_axi = devm_clk_get(&pdev->dev, "axi");
rc = clk_set_rate(gdc_dev->clk_axi, 800000000);
#else
clk_cntl = of_iomap(pdev->dev.of_node, 1);
iowrite32((3<<25)|(1<<24)|(0<<16)|(3<<9)|(1<<8)|(0<<0), clk_cntl);
pd_cntl = of_iomap(pdev->dev.of_node, 2);
reg_value = ioread32(pd_cntl);
LOG(LOG_DEBUG, "pd_cntl=%x\n", reg_value);
reg_value = reg_value & (~(3<<18));
LOG(LOG_DEBUG, "pd_cntl=%x\n", reg_value);
iowrite32(reg_value, pd_cntl);
#endif
g_gdc_dev = gdc_dev;
return misc_register(&meson_gdc_dev);
}
static int gdc_platform_remove(struct platform_device *pdev)
{
device_remove_file(&pdev->dev, &dev_attr_gdc_reg);
device_remove_file(&pdev->dev, &dev_attr_firmware1);
misc_deregister(&meson_gdc_dev);
return 0;
}
static struct platform_driver gdc_platform_driver = {
.driver = {
.name = "gdc",
.owner = THIS_MODULE,
.of_match_table = gdc_dt_match,
},
.probe = gdc_platform_probe,
.remove = gdc_platform_remove,
};
module_platform_driver(gdc_platform_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Amlogic Multimedia");

View File

@@ -0,0 +1,174 @@
/*
* drivers/amlogic/media/gdc/inc/api/gdc_api.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __GDC_API_H__
#define __GDC_API_H__
#include <linux/of_address.h>
#define NV12 1
#define YV12 2
// each configuration addresses and size
struct gdc_config {
uint32_t format;
uint32_t config_addr; //gdc config address
uint32_t config_size; //gdc config size in 32bit
uint32_t input_width; //gdc input width resolution
uint32_t input_height; //gdc input height resolution
uint32_t input_y_stride; //gdc input y stride resolution
uint32_t input_c_stride; //gdc input uv stride
uint32_t output_width; //gdc output width resolution
uint32_t output_height; //gdc output height resolution
uint32_t output_y_stride; //gdc output y stride
uint32_t output_c_stride; //gdc output uv stride
};
// overall gdc settings and state
struct gdc_settings {
uint32_t magic;
//writing/reading to gdc base address, currently not read by api
uint32_t base_gdc;
//array of gdc configuration and sizes
struct gdc_config gdc_config;
//update this index for new config
//int gdc_config_total;
//start memory to write gdc output framse
uint32_t buffer_addr;
//size of memory output frames to determine
//if it is enough and can do multiple write points
uint32_t buffer_size;
//current output address of gdc
uint32_t current_addr;
//set when expecting an interrupt from gdc
int32_t is_waiting_gdc;
int32_t in_fd; //input buffer's share fd
int32_t out_fd; //output buffer's share fd
//input address for y and u, v planes
uint32_t y_base_addr;
union {
uint32_t uv_base_addr;
uint32_t u_base_addr;
};
uint32_t v_base_addr;
//opaque address in ddr added with offset to
//write the gdc config sequence
void *ddr_mem;
//when inititialised this callback will be called
//to update frame buffer addresses and offsets
void (*get_frame_buffer)(uint32_t y_base_addr,
uint32_t uv_base_addr,
uint32_t y_line_offset,
uint32_t uv_line_offset);
void *fh;
};
#define GDC_IOC_MAGIC 'G'
#define GDC_PROCESS _IOW(GDC_IOC_MAGIC, 0x00, struct gdc_settings)
#define GDC_PROCESS_NO_BLOCK _IOW(GDC_IOC_MAGIC, 0x01, struct gdc_settings)
/**
* Configure the output gdc configuration
*
* address/size and buffer address/size; and resolution.
*
* More than one gdc settings can be accessed by index to a gdc_config_t.
*
* @param gdc_settings - overall gdc settings and state
* @param gdc_config_num - selects the current gdc config to be applied
*
* @return 0 - success
* -1 - fail.
*/
int gdc_init(struct gdc_settings *gdc_settings);
/**
* This function stops the gdc block
*
* @param gdc_settings - overall gdc settings and state
*
*/
void gdc_stop(struct gdc_settings *gdc_settings);
/**
* This function starts the gdc block
*
* Writing 0->1 transition is necessary for trigger
*
* @param gdc_settings - overall gdc settings and state
*
*/
void gdc_start(struct gdc_settings *gdc_settings);
/**
* This function points gdc to
*
* its input resolution and yuv address and offsets
*
* Shown inputs to GDC are Y and UV plane address and offsets
*
* @param gdc_settings - overall gdc settings and state
* @param active_width - input width resolution
* @param active_height - input height resolution
* @param y_base_addr - input Y base address
* @param uv_base_addr - input UV base address
* @param y_line_offset - input Y line buffer offset
* @param uv_line_offset- input UV line buffer offer
*
* @return 0 - success
* -1 - no interrupt from GDC.
*/
int gdc_process(struct gdc_settings *gdc_settings,
uint32_t y_base_addr,
uint32_t uv_base_addr);
int gdc_process_yuv420p(struct gdc_settings *gdc_settings,
uint32_t y_base_addr,
uint32_t u_base_addr,
uint32_t v_base_addr);
/**
* This function gets the GDC output frame addresses
*
* and offsets and updates the frame buffer via callback
*
* if it is available Shown ouputs to GDC are
*
* Y and UV plane address and offsets
*
* @param gdc_settings - overall gdc settings and state
*
* @return 0 - success
* -1 - unexpected interrupt from GDC.
*/
int gdc_get_frame(struct gdc_settings *gdc_settings);
/**
* This function points gdc to its input resolution
*
* and yuv address and offsets
*
* Shown inputs to GDC are Y and UV plane address and offsets
*
* @param gdc_settings - overall gdc settings and state
*
* @return 0 - success
* -1 - no interrupt from GDC.
*/
int gdc_run(struct gdc_settings *g);
int32_t init_gdc_io(struct device_node *dn);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
/*
* drivers/amlogic/media/gdc/inc/sys/system_gdc_io.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __SYSTEM_GDC_IO_H__
#define __SYSTEM_GDC_IO_H__
/**
* Read 32 bit word from gdc memory
*
* This function returns a 32 bits word from GDC memory with a given offset.
*
* @param addr - the offset in GDC memory to read 32 bits word.
*
* @return 32 bits memory value
*/
uint32_t system_gdc_read_32(uint32_t addr);
/**
* Write 32 bits word to gdc memory
*
* This function writes a 32 bits word to GDC memory with a given offset.
*
* @param addr - the offset in GDC memory to write data.
* @param data - data to be written
*/
void system_gdc_write_32(uint32_t addr, uint32_t data);
#endif /* __SYSTEM_GDC_IO_H__ */

View File

@@ -0,0 +1,55 @@
/*
* drivers/amlogic/media/gdc/inc/sys/system_log.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __SYSTEM_LOG_H__
#define __SYSTEM_LOG_H__
//changeable logs
#include <linux/kernel.h>
#define FW_LOG_LEVEL LOG_MAX
enum log_level_e {
LOG_NOTHING,
LOG_EMERG,
LOG_ALERT,
LOG_CRIT,
LOG_ERR,
LOG_WARNING,
LOG_NOTICE,
LOG_INFO,
LOG_DEBUG,
LOG_IRQ,
LOG_MAX
};
extern const char *const gdc_log_level[LOG_MAX];
#define FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#if 1
#define LOG(level, fmt, arg...) \
do { \
if ((level) <= FW_LOG_LEVEL) \
trace_printk("%s: %s(%d) %s: " fmt "\n",\
FILE, __func__, __LINE__, \
gdc_log_level[level], ## arg); \
} while (0)
#else
#define LOG(...)
#endif
#endif // __SYSTEM_LOG_H__

View File

@@ -0,0 +1,280 @@
/*
* drivers/amlogic/media/gdc/src/fw_lib/acamera_gdc.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
//needed for gdc/gdc configuration
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
//data types and prototypes
#include "gdc_api.h"
#include "system_log.h"
#include "gdc_config.h"
/**
* Configure the output gdc configuration address/size
*
* and buffer address/size; and resolution.
*
* More than one gdc settings can be accessed by index to a gdc_config_t.
*
* @param gdc_settings - overall gdc settings and state
* @param gdc_config_num - selects the current gdc config to be applied
*
* @return 0 - success
* -1 - fail.
*/
int gdc_init(struct gdc_settings *gdc_settings)
{
gdc_settings->is_waiting_gdc = 0;
gdc_settings->current_addr = gdc_settings->buffer_addr;
if ((gdc_settings->gdc_config.output_width == 0)
|| (gdc_settings->gdc_config.output_height == 0)) {
LOG(LOG_ERR, "Wrong GDC output resolution.\n");
return -1;
}
//stop gdc
gdc_start_flag_write(0);
//set the configuration address and size to the gdc block
gdc_config_addr_write(gdc_settings->gdc_config.config_addr);
gdc_config_size_write(gdc_settings->gdc_config.config_size);
//set the gdc output resolution
gdc_dataout_width_write(gdc_settings->gdc_config.output_width);
gdc_dataout_height_write(gdc_settings->gdc_config.output_height);
return 0;
}
/**
* This function stops the gdc block
*
* @param gdc_settings - overall gdc settings and state
*
*/
void gdc_stop(struct gdc_settings *gdc_settings)
{
gdc_settings->is_waiting_gdc = 0;
gdc_start_flag_write(0);
}
/**
* This function starts the gdc block
*
* Writing 0->1 transition is necessary for trigger
*
* @param gdc_settings - overall gdc settings and state
*
*/
void gdc_start(struct gdc_settings *gdc_settings)
{
gdc_start_flag_write(0); //do a stop for sync
gdc_start_flag_write(1);
gdc_settings->is_waiting_gdc = 1;
}
/**
* This function points gdc to its input resolution
*
* and yuv address and offsets
*
* Shown inputs to GDC are Y and UV plane address and offsets
*
* @param gdc_settings - overall gdc settings and state
* @param active_width - input width resolution
* @param active_height - input height resolution
* @param y_base_addr - input Y base address
* @param uv_base_addr - input UV base address
* @param y_line_offset - input Y line buffer offset
* @param uv_line_offset- input UV line buffer offer
*
* @return 0 - success
* -1 - no interrupt from GDC.
*/
int gdc_process(struct gdc_settings *gdc_settings,
uint32_t y_base_addr, uint32_t uv_base_addr)
{
uint32_t gdc_out_base_addr = gdc_settings->current_addr;
uint32_t active_width = gdc_settings->gdc_config.output_width;
uint32_t active_height = gdc_settings->gdc_config.output_height;
uint32_t y_line_offset = active_width; //420 format
uint32_t uv_line_offset = active_width; //420 format
if (gdc_settings->is_waiting_gdc) {
gdc_start_flag_write(0);
LOG(LOG_CRIT, "No interrupt Still waiting...\n");
gdc_start_flag_write(1);
return -1;
}
LOG(LOG_DEBUG, "starting GDC process.\n");
gdc_datain_width_write(active_width);
gdc_datain_height_write(active_height);
//input y plane
gdc_data1in_addr_write(y_base_addr);
gdc_data1in_line_offset_write(y_line_offset);
//input uv plane
gdc_data2in_addr_write(uv_base_addr);
gdc_data2in_line_offset_write(uv_line_offset);
//gdc y output
gdc_data1out_addr_write(gdc_out_base_addr);
gdc_data1out_line_offset_write(y_line_offset);
//gdc uv output
gdc_out_base_addr += active_height * y_line_offset;
gdc_data2out_addr_write(gdc_out_base_addr);
gdc_data2out_line_offset_write(uv_line_offset);
gdc_start(gdc_settings);
return 0;
}
/**
* This function points gdc to its input resolution
*
* and yuv address and offsets
*
* Shown inputs to GDC are Y and UV plane address and offsets
*
* @param gdc_settings - overall gdc settings and state
* @param active_width - input width resolution
* @param active_height - input height resolution
* @param y_base_addr - input Y base address
* @param uv_base_addr - input UV base address
* @param y_line_offset - input Y line buffer offset
* @param uv_line_offset- input UV line buffer offer
*
* @return 0 - success
* -1 - no interrupt from GDC.
*/
int gdc_process_yuv420p(struct gdc_settings *gdc_settings,
uint32_t y_base_addr, uint32_t u_base_addr, uint32_t v_base_addr)
{
struct gdc_config *gc = &gdc_settings->gdc_config;
uint32_t gdc_out_base_addr = gdc_settings->current_addr;
uint32_t input_width = gc->input_width;
uint32_t input_height = gc->input_height;
uint32_t input_stride = gc->input_y_stride;
uint32_t input_u_stride = gc->input_c_stride;
uint32_t input_v_stride = gc->input_c_stride;
LOG(LOG_DEBUG, "is_waiting_gdc=%d\n", gdc_settings->is_waiting_gdc);
if (gdc_settings->is_waiting_gdc) {
gdc_start_flag_write(0);
LOG(LOG_CRIT, "No interrupt Still waiting...\n");
gdc_start_flag_write(1);
return -1;
}
/////
LOG(LOG_DEBUG, "starting GDC process.\n");
//already set in gdc_init
//uint32_t output_width = gc->output_width;
uint32_t output_height = gc->output_height;
uint32_t output_stride = gc->output_y_stride;
uint32_t output_u_stride = gc->output_c_stride;
uint32_t output_v_stride = gc->output_c_stride;
gdc_datain_width_write(input_width);
gdc_datain_height_write(input_height);
//input y plane
gdc_data1in_addr_write(y_base_addr);
gdc_data1in_line_offset_write(input_stride);
//input u plane
gdc_data2in_addr_write(u_base_addr);
gdc_data2in_line_offset_write(input_u_stride);
//input v plane
gdc_data3in_addr_write(v_base_addr);
gdc_data3in_line_offset_write(input_v_stride);
//gdc y output
gdc_data1out_addr_write(gdc_out_base_addr);
gdc_data1out_line_offset_write(output_stride);
//gdc u output
gdc_out_base_addr += output_height * output_stride;
gdc_data2out_addr_write(gdc_out_base_addr);
gdc_data2out_line_offset_write(output_u_stride);
//gdc v output
gdc_out_base_addr += output_height * output_u_stride / 2;
gdc_data3out_addr_write(gdc_out_base_addr);
gdc_data3out_line_offset_write(output_v_stride);
gdc_start(gdc_settings);
return 0;
}
/**
* This function gets the GDC output frame addresses
*
* and offsets and updates the frame buffer via callback
*
* if it is available Shown ouputs to GDC are
*
* Y and UV plane address and offsets
*
* @param gdc_settings - overall gdc settings and state
*
* @return 0 - success
* -1 - unexpected interrupt from GDC.
*/
int gdc_get_frame(struct gdc_settings *gdc_settings)
{
struct mgdc_fh_s *fh = gdc_settings->fh;
uint32_t y;
uint32_t y_offset;
uint32_t uv;
uint32_t uv_offset;
if (!gdc_settings->is_waiting_gdc) {
LOG(LOG_CRIT, "Unexpected interrupt from GDC.\n");
return -1;
}
////
wake_up_interruptible(&fh->irq_queue);
//pass the frame buffer parameters if callback is available
if (gdc_settings->get_frame_buffer) {
y = gdc_data1out_addr_read();
y_offset = gdc_data1out_line_offset_read();
uv = gdc_data2out_addr_read();
uv_offset = gdc_data2out_line_offset_read();
gdc_settings->get_frame_buffer(y,
uv, y_offset, uv_offset);
}
//done of the current frame and stop gdc block
gdc_stop(gdc_settings);
//spin_unlock_irqrestore(&gdev->slock, flags);
return 0;
}

View File

@@ -0,0 +1,66 @@
/*
* drivers/amlogic/media/gdc/src/platform/system_gdc_io.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "system_log.h"
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/errno.h>
#include <linux/kernel.h>
static void *p_hw_base;
int32_t init_gdc_io(struct device_node *dn)
{
p_hw_base = of_iomap(dn, 0);
pr_info("reg base = %p\n", p_hw_base);
if (!p_hw_base) {
LOG(LOG_DEBUG, "failed to map register, %p\n", p_hw_base);
return -1;
}
return 0;
}
void close_gdc_io(struct device_node *dn)
{
LOG(LOG_DEBUG, "IO functionality has been closed");
}
uint32_t system_gdc_read_32(uint32_t addr)
{
uint32_t result = 0;
if (p_hw_base == NULL)
LOG(LOG_ERR, "Failed to base address %d\n", addr);
result = ioread32(p_hw_base + addr);
LOG(LOG_DEBUG, "r [0x%04x]= %08x\n", addr, result);
return result;
}
void system_gdc_write_32(uint32_t addr, uint32_t data)
{
if (p_hw_base == NULL)
LOG(LOG_ERR, "Failed to write %d to addr %d\n", data, addr);
void *ptr = (void *)(p_hw_base + addr);
iowrite32(data, ptr);
LOG(LOG_DEBUG, "w [0x%04x]= %08x\n", addr, data);
}

View File

@@ -0,0 +1,21 @@
/*
* drivers/amlogic/media/gdc/src/platform/system_log.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "system_log.h"
const char *const gdc_log_level[LOG_MAX] = {"",
"EMERG", "ALERT", "CRIT", "ERR", "WARNING",
"NOTICE", "INFO", "LOG_DEBUG", "LOG_IRQ"};