blob: 0ecaa307eb5aa3ccca53f40d16c2660e79bb296f [file] [log] [blame]
/*
* 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/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include <linux/of_reserved_mem.h>
#include <linux/dma-buf.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"
#include "gdc_dmabuf.h"
unsigned int gdc_log_level;
struct gdc_manager_s gdc_manager;
static const struct of_device_id gdc_dt_match[] = {
{.compatible = "amlogic, g12b-gdc"},
{} };
MODULE_DEVICE_TABLE(of, gdc_dt_match);
static void meson_gdc_cache_flush(struct device *dev,
dma_addr_t addr,
size_t size);
//////
static int meson_gdc_open(struct inode *inode, struct file *file)
{
struct meson_gdc_dev_t *gdc_dev = gdc_manager.gdc_dev;
struct mgdc_fh_s *fh = NULL;
char ion_client_name[32];
int rc = 0;
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (fh == NULL) {
gdc_log(LOG_DEBUG, "devm alloc failed\n");
return -ENOMEM;
}
get_task_comm(fh->task_comm, current);
gdc_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;
gdc_log(LOG_INFO, "Success open\n");
return rc;
}
static int meson_gdc_release(struct inode *inode, struct file *file)
{
struct mgdc_fh_s *fh = file->private_data;
struct page *cma_pages = NULL;
bool rc = false;
int ret = 0;
if (fh->ion_client) {
ion_client_destroy(fh->ion_client);
fh->ion_client = NULL;
}
if (fh->i_kaddr != 0 && fh->i_len != 0) {
cma_pages = virt_to_page(fh->i_kaddr);
rc = dma_release_from_contiguous(&fh->gdev->pdev->dev,
cma_pages,
fh->i_len >> PAGE_SHIFT);
if (rc == false) {
ret = ret - 1;
gdc_log(LOG_ERR, "Failed to release input buff\n");
}
fh->i_kaddr = NULL;
fh->i_paddr = 0;
fh->i_len = 0;
}
if (fh->o_kaddr != 0 && fh->o_len != 0) {
cma_pages = virt_to_page(fh->o_kaddr);
rc = dma_release_from_contiguous(&fh->gdev->pdev->dev,
cma_pages,
fh->o_len >> PAGE_SHIFT);
if (rc == false) {
ret = ret - 1;
gdc_log(LOG_ERR, "Failed to release output buff\n");
}
fh->o_kaddr = NULL;
fh->o_paddr = 0;
fh->o_len = 0;
}
if (fh->c_kaddr != 0 && fh->c_len != 0) {
cma_pages = virt_to_page(fh->c_kaddr);
rc = dma_release_from_contiguous(&fh->gdev->pdev->dev,
cma_pages,
fh->c_len >> PAGE_SHIFT);
if (rc == false) {
ret = ret - 1;
gdc_log(LOG_ERR, "Failed to release config buff\n");
}
fh->c_kaddr = NULL;
fh->c_paddr = 0;
fh->c_len = 0;
}
kfree(fh);
fh = NULL;
if (ret == 0)
gdc_log(LOG_INFO, "Success release\n");
else
gdc_log(LOG_ERR, "Error release\n");
return ret;
}
static long meson_gdc_set_buff(void *f_fh,
struct page *cma_pages,
unsigned long len)
{
int ret = 0;
struct mgdc_fh_s *fh = NULL;
if (f_fh == NULL || cma_pages == NULL || len == 0) {
gdc_log(LOG_ERR, "Error input param\n");
return -EINVAL;
}
fh = f_fh;
switch (fh->mmap_type) {
case INPUT_BUFF_TYPE:
if (fh->i_paddr != 0 && fh->i_kaddr != NULL)
return -EAGAIN;
fh->i_paddr = page_to_phys(cma_pages);
fh->i_kaddr = phys_to_virt(fh->i_paddr);
fh->i_len = len;
break;
case OUTPUT_BUFF_TYPE:
if (fh->o_paddr != 0 && fh->o_kaddr != NULL)
return -EAGAIN;
fh->o_paddr = page_to_phys(cma_pages);
fh->o_kaddr = phys_to_virt(fh->o_paddr);
fh->o_len = len;
meson_gdc_cache_flush(&fh->gdev->pdev->dev,
fh->o_paddr, fh->o_len);
break;
case CONFIG_BUFF_TYPE:
if (fh->c_paddr != 0 && fh->c_kaddr != NULL)
return -EAGAIN;
fh->c_paddr = page_to_phys(cma_pages);
fh->c_kaddr = phys_to_virt(fh->c_paddr);
fh->c_len = len;
break;
default:
gdc_log(LOG_ERR, "Error mmap type:0x%x\n", fh->mmap_type);
ret = -EINVAL;
break;
}
return ret;
}
static long meson_gdc_set_input_addr(uint32_t start_addr,
struct gdc_cmd_s *gdc_cmd)
{
struct gdc_config_s *gc = NULL;
if (gdc_cmd == NULL || start_addr == 0) {
gdc_log(LOG_ERR, "Error input param\n");
return -EINVAL;
}
gc = &gdc_cmd->gdc_config;
switch (gc->format) {
case NV12:
gdc_cmd->y_base_addr = start_addr;
gdc_cmd->uv_base_addr = start_addr +
gc->input_y_stride * gc->input_height;
break;
case YV12:
gdc_cmd->y_base_addr = start_addr;
gdc_cmd->u_base_addr = start_addr +
gc->input_y_stride * gc->input_height;
gdc_cmd->v_base_addr = gdc_cmd->u_base_addr +
gc->input_c_stride * gc->input_height / 2;
break;
case Y_GREY:
gdc_cmd->y_base_addr = start_addr;
gdc_cmd->u_base_addr = 0;
gdc_cmd->v_base_addr = 0;
break;
case YUV444_P:
gdc_cmd->y_base_addr = start_addr;
gdc_cmd->u_base_addr = start_addr +
gc->input_y_stride * gc->input_height;
gdc_cmd->v_base_addr = gdc_cmd->u_base_addr +
gc->input_c_stride * gc->input_height;
break;
case RGB444_P:
gdc_cmd->y_base_addr = start_addr;
gdc_cmd->u_base_addr = start_addr +
gc->input_y_stride * gc->input_height;
gdc_cmd->v_base_addr = gdc_cmd->u_base_addr +
gc->input_c_stride * gc->input_height;
break;
default:
gdc_log(LOG_ERR, "Error config format\n");
return -EINVAL;
break;
}
return 0;
}
static void meson_gdc_dma_flush(struct device *dev,
dma_addr_t addr,
size_t size)
{
if (dev == NULL) {
gdc_log(LOG_ERR, "Error input param");
return;
}
dma_sync_single_for_device(dev, addr, size, DMA_TO_DEVICE);
}
static void meson_gdc_cache_flush(struct device *dev,
dma_addr_t addr,
size_t size)
{
if (dev == NULL) {
gdc_log(LOG_ERR, "Error input param");
return;
}
dma_sync_single_for_cpu(dev, addr, size, DMA_FROM_DEVICE);
}
static long meson_gdc_dma_map(struct gdc_dma_cfg *cfg)
{
long ret = -1;
int fd = -1;
struct dma_buf *dbuf = NULL;
struct dma_buf_attachment *d_att = NULL;
struct sg_table *sg = NULL;
void *vaddr = NULL;
struct device *dev = NULL;
enum dma_data_direction dir;
if (cfg == NULL || (cfg->fd < 0) || cfg->dev == NULL) {
gdc_log(LOG_ERR, "Error input param");
return -EINVAL;
}
fd = cfg->fd;
dev = cfg->dev;
dir = cfg->dir;
dbuf = dma_buf_get(fd);
if (dbuf == NULL) {
gdc_log(LOG_ERR, "Failed to get dma buffer");
return -EINVAL;
}
d_att = dma_buf_attach(dbuf, dev);
if (d_att == NULL) {
gdc_log(LOG_ERR, "Failed to set dma attach");
goto attach_err;
}
sg = dma_buf_map_attachment(d_att, dir);
if (sg == NULL) {
gdc_log(LOG_ERR, "Failed to get dma sg");
goto map_attach_err;
}
ret = dma_buf_begin_cpu_access(dbuf, dir);
if (ret != 0) {
gdc_log(LOG_ERR, "Failed to access dma buff");
goto access_err;
}
vaddr = dma_buf_vmap(dbuf);
if (vaddr == NULL) {
gdc_log(LOG_ERR, "Failed to vmap dma buf");
goto vmap_err;
}
cfg->dbuf = dbuf;
cfg->attach = d_att;
cfg->vaddr = vaddr;
cfg->sg = sg;
return ret;
vmap_err:
dma_buf_end_cpu_access(dbuf, dir);
access_err:
dma_buf_unmap_attachment(d_att, sg, dir);
map_attach_err:
dma_buf_detach(dbuf, d_att);
attach_err:
dma_buf_put(dbuf);
return ret;
}
static void meson_gdc_dma_unmap(struct gdc_dma_cfg *cfg)
{
int fd = -1;
struct dma_buf *dbuf = NULL;
struct dma_buf_attachment *d_att = NULL;
struct sg_table *sg = NULL;
void *vaddr = NULL;
struct device *dev = NULL;
enum dma_data_direction dir;
if (cfg == NULL || (cfg->fd < 0) || cfg->dev == NULL
|| cfg->dbuf == NULL || cfg->vaddr == NULL
|| cfg->attach == NULL || cfg->sg == NULL) {
gdc_log(LOG_ERR, "Error input param");
return;
}
fd = cfg->fd;
dev = cfg->dev;
dir = cfg->dir;
dbuf = cfg->dbuf;
vaddr = cfg->vaddr;
d_att = cfg->attach;
sg = cfg->sg;
dma_buf_vunmap(dbuf, vaddr);
dma_buf_end_cpu_access(dbuf, dir);
dma_buf_unmap_attachment(d_att, sg, dir);
dma_buf_detach(dbuf, d_att);
dma_buf_put(dbuf);
}
static long meson_gdc_init_dma_addr(struct mgdc_fh_s *fh,
struct gdc_settings *gs)
{
long ret = -1;
struct gdc_dma_cfg *dma_cfg = NULL;
struct gdc_cmd_s *gdc_cmd = &fh->gdc_cmd;
struct gdc_config_s *gc = &gdc_cmd->gdc_config;
if (fh == NULL || gs == NULL) {
gdc_log(LOG_ERR, "Error input param\n");
return -EINVAL;
}
switch (gc->format) {
case NV12:
dma_cfg = &fh->y_dma_cfg;
memset(dma_cfg, 0, sizeof(*dma_cfg));
dma_cfg->dir = DMA_TO_DEVICE;
dma_cfg->dev = &fh->gdev->pdev->dev;
dma_cfg->fd = gs->y_base_fd;
ret = meson_gdc_dma_map(dma_cfg);
if (ret != 0) {
gdc_log(LOG_ERR, "Failed to get map dma buff");
return ret;
}
gdc_cmd->y_base_addr = virt_to_phys(dma_cfg->vaddr);
dma_cfg = &fh->uv_dma_cfg;
memset(dma_cfg, 0, sizeof(*dma_cfg));
dma_cfg->dir = DMA_TO_DEVICE;
dma_cfg->dev = &fh->gdev->pdev->dev;
dma_cfg->fd = gs->uv_base_fd;
ret = meson_gdc_dma_map(dma_cfg);
if (ret != 0) {
gdc_log(LOG_ERR, "Failed to get map dma buff");
return ret;
}
gdc_cmd->uv_base_addr = virt_to_phys(dma_cfg->vaddr);
break;
case Y_GREY:
dma_cfg = &fh->y_dma_cfg;
memset(dma_cfg, 0, sizeof(*dma_cfg));
dma_cfg->dir = DMA_TO_DEVICE;
dma_cfg->dev = &fh->gdev->pdev->dev;
dma_cfg->fd = gs->y_base_fd;
ret = meson_gdc_dma_map(dma_cfg);
if (ret != 0) {
gdc_log(LOG_ERR, "Failed to get map dma buff");
return ret;
}
gdc_cmd->y_base_addr = virt_to_phys(dma_cfg->vaddr);
gdc_cmd->uv_base_addr = 0;
break;
default:
gdc_log(LOG_ERR, "Error image format");
break;
}
return ret;
}
static void meson_gdc_deinit_dma_addr(struct mgdc_fh_s *fh)
{
struct gdc_dma_cfg *dma_cfg = NULL;
struct gdc_cmd_s *gdc_cmd = &fh->gdc_cmd;
struct gdc_config_s *gc = &gdc_cmd->gdc_config;
if (fh == NULL) {
gdc_log(LOG_ERR, "Error input param\n");
return;
}
switch (gc->format) {
case NV12:
dma_cfg = &fh->y_dma_cfg;
meson_gdc_dma_unmap(dma_cfg);
dma_cfg = &fh->uv_dma_cfg;
meson_gdc_dma_unmap(dma_cfg);
break;
case Y_GREY:
dma_cfg = &fh->y_dma_cfg;
meson_gdc_dma_unmap(dma_cfg);
break;
default:
gdc_log(LOG_ERR, "Error image format");
break;
}
}
static int gdc_buffer_alloc(struct gdc_dmabuf_req_s *gdc_req_buf)
{
struct device *dev;
dev = &(gdc_manager.gdc_dev->pdev->dev);
return gdc_dma_buffer_alloc(gdc_manager.buffer,
dev, gdc_req_buf);
}
static int gdc_buffer_export(struct gdc_dmabuf_exp_s *gdc_exp_buf)
{
return gdc_dma_buffer_export(gdc_manager.buffer, gdc_exp_buf);
}
static int gdc_buffer_free(int index)
{
return gdc_dma_buffer_free(gdc_manager.buffer, index);
}
static void gdc_buffer_dma_flush(int dma_fd)
{
struct device *dev;
dev = &(gdc_manager.gdc_dev->pdev->dev);
gdc_dma_buffer_dma_flush(dev, dma_fd);
}
static void gdc_buffer_cache_flush(int dma_fd)
{
struct device *dev;
dev = &(gdc_manager.gdc_dev->pdev->dev);
gdc_dma_buffer_cache_flush(dev, dma_fd);
}
static long gdc_process_input_dma_info(struct mgdc_fh_s *fh,
struct gdc_settings_ex *gs_ex)
{
long ret = -1;
unsigned long addr;
struct aml_dma_cfg *cfg = NULL;
struct gdc_cmd_s *gdc_cmd = &fh->gdc_cmd;
struct gdc_config_s *gc = &gdc_cmd->gdc_config;
if (fh == NULL || gs_ex == NULL) {
gdc_log(LOG_ERR, "Error input param\n");
return -EINVAL;
}
switch (gc->format) {
case NV12:
if (gs_ex->input_buffer.plane_number == 1) {
cfg = &fh->dma_cfg.input_cfg_plane1;
cfg->fd = gs_ex->input_buffer.y_base_fd;
cfg->dev = &fh->gdev->pdev->dev;
cfg->dir = DMA_TO_DEVICE;
ret = gdc_dma_buffer_get_phys(cfg, &addr);
if (ret < 0) {
gdc_log(LOG_ERR,
"dma import input fd %d failed\n",
gs_ex->input_buffer.y_base_fd);
return -EINVAL;
}
ret = meson_gdc_set_input_addr(addr, gdc_cmd);
if (ret != 0) {
gdc_log(LOG_ERR, "set input addr failed\n");
return -EINVAL;
}
gdc_log(LOG_INFO, "1 plane get input addr=%x\n",
gdc_cmd->y_base_addr);
meson_gdc_dma_flush(&fh->gdev->pdev->dev,
gdc_cmd->y_base_addr,
gc->input_y_stride * gc->input_height);
} else if (gs_ex->input_buffer.plane_number == 2) {
cfg = &fh->dma_cfg.input_cfg_plane1;
cfg->fd = gs_ex->input_buffer.y_base_fd;
cfg->dev = &fh->gdev->pdev->dev;
cfg->dir = DMA_TO_DEVICE;
ret = gdc_dma_buffer_get_phys(cfg, &addr);
if (ret < 0) {
gdc_log(LOG_ERR,
"dma import input fd %d failed\n",
gs_ex->input_buffer.y_base_fd);
return -EINVAL;
}
gdc_cmd->y_base_addr = addr;
meson_gdc_dma_flush(&fh->gdev->pdev->dev,
gdc_cmd->y_base_addr,
gc->input_y_stride * gc->input_height);
cfg = &fh->dma_cfg.input_cfg_plane2;
cfg->fd = gs_ex->input_buffer.uv_base_fd;
cfg->dev = &fh->gdev->pdev->dev;
cfg->dir = DMA_TO_DEVICE;
ret = gdc_dma_buffer_get_phys(cfg, &addr);
if (ret < 0) {
gdc_log(LOG_ERR,
"dma import input fd %d failed\n",
gs_ex->input_buffer.uv_base_fd);
return -EINVAL;
}
gdc_cmd->uv_base_addr = addr;
meson_gdc_dma_flush(&fh->gdev->pdev->dev,
gdc_cmd->uv_base_addr,
gc->input_y_stride * gc->input_height / 2);
gdc_log(LOG_INFO, "2 plane get input addr=%x\n",
gdc_cmd->y_base_addr);
gdc_log(LOG_INFO, "2 plane get input addr=%x\n",
gdc_cmd->uv_base_addr);
}
break;
case Y_GREY:
cfg = &fh->dma_cfg.input_cfg_plane1;
cfg->fd = gs_ex->input_buffer.y_base_fd;
cfg->dev = &(fh->gdev->pdev->dev);
cfg->dir = DMA_TO_DEVICE;
ret = gdc_dma_buffer_get_phys(cfg, &addr);
if (ret < 0) {
gdc_log(LOG_ERR,
"dma import input fd %d failed\n",
gs_ex->input_buffer.shared_fd);
return -EINVAL;
}
gdc_cmd->y_base_addr = addr;
gdc_cmd->uv_base_addr = 0;
meson_gdc_dma_flush(&fh->gdev->pdev->dev,
gdc_cmd->y_base_addr,
gc->input_y_stride * gc->input_height);
break;
default:
gdc_log(LOG_ERR, "Error image format");
break;
}
return 0;
}
static long gdc_process_ex_info(struct mgdc_fh_s *fh,
struct gdc_settings_ex *gs_ex)
{
long ret;
unsigned long addr = 0;
size_t len;
struct aml_dma_cfg *cfg = NULL;
struct gdc_cmd_s *gdc_cmd = &fh->gdc_cmd;
if (fh == NULL || gs_ex == NULL) {
gdc_log(LOG_ERR, "Error input param\n");
return -EINVAL;
}
memcpy(&(gdc_cmd->gdc_config), &(gs_ex->gdc_config),
sizeof(struct gdc_config_s));
gdc_cmd->fh = fh;
if (gs_ex->output_buffer.mem_alloc_type == AML_GDC_MEM_ION) {
/* ion alloc */
ret = meson_ion_share_fd_to_phys(fh->ion_client,
gs_ex->output_buffer.shared_fd,
(ion_phys_addr_t *)&addr, &len);
if (ret < 0) {
gdc_log(LOG_ERR, "ion import out fd %d failed\n",
gs_ex->output_buffer.shared_fd);
return -EINVAL;
}
} else if (gs_ex->output_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF) {
/* dma alloc */
cfg = &fh->dma_cfg.output_cfg;
cfg->fd = gs_ex->output_buffer.y_base_fd;
cfg->dev = &(gdc_manager.gdc_dev->pdev->dev);
cfg->dir = DMA_FROM_DEVICE;
ret = gdc_dma_buffer_get_phys(cfg, &addr);
if (ret < 0) {
gdc_log(LOG_ERR, "dma import out fd %d failed\n",
gs_ex->output_buffer.y_base_fd);
return -EINVAL;
}
}
gdc_log(LOG_INFO, "%s, output addr=%lx\n", __func__, addr);
gdc_cmd->base_gdc = 0;
gdc_cmd->buffer_addr = addr;
gdc_cmd->current_addr = gdc_cmd->buffer_addr;
if (gs_ex->config_buffer.mem_alloc_type == AML_GDC_MEM_ION) {
/* ion alloc */
ret = meson_ion_share_fd_to_phys(fh->ion_client,
gs_ex->config_buffer.shared_fd,
(ion_phys_addr_t *)&addr, &len);
if (ret < 0) {
gdc_log(LOG_ERR, "ion import config fd %d failed\n",
gs_ex->config_buffer.shared_fd);
return -EINVAL;
}
} else if (gs_ex->config_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF) {
/* dma alloc */
cfg = &fh->dma_cfg.config_cfg;
cfg->fd = gs_ex->config_buffer.y_base_fd;
cfg->dev = &(gdc_manager.gdc_dev->pdev->dev);
cfg->dir = DMA_TO_DEVICE;
ret = gdc_dma_buffer_get_phys(cfg, &addr);
if (ret < 0) {
gdc_log(LOG_ERR, "dma import config fd %d failed\n",
gs_ex->config_buffer.shared_fd);
return -EINVAL;
}
}
gdc_cmd->gdc_config.config_addr = addr;
gdc_log(LOG_INFO, "%s, config addr=%lx\n", __func__, addr);
if (gs_ex->input_buffer.mem_alloc_type == AML_GDC_MEM_ION) {
/* ion alloc */
ret = meson_ion_share_fd_to_phys(fh->ion_client,
gs_ex->input_buffer.shared_fd,
(ion_phys_addr_t *)&addr, &len);
if (ret < 0) {
gdc_log(LOG_ERR, "ion import input fd %d failed\n",
gs_ex->input_buffer.shared_fd);
return -EINVAL;
}
ret = meson_gdc_set_input_addr(addr, gdc_cmd);
if (ret != 0) {
gdc_log(LOG_ERR, "set input addr failed\n");
return -EINVAL;
}
} else if (gs_ex->input_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF) {
/* dma alloc */
ret = gdc_process_input_dma_info(fh, gs_ex);
if (ret < 0)
return -EINVAL;
}
gdc_log(LOG_INFO, "%s, input addr=%x\n",
__func__, fh->gdc_cmd.y_base_addr);
mutex_lock(&fh->gdev->d_mutext);
if (gs_ex->config_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF)
gdc_buffer_dma_flush(gs_ex->config_buffer.shared_fd);
ret = gdc_run(gdc_cmd);
if (ret < 0)
gdc_log(LOG_ERR, "gdc process failed ret = %ld\n", ret);
ret = wait_for_completion_timeout(&fh->gdev->d_com,
msecs_to_jiffies(40));
if (ret == 0)
gdc_log(LOG_ERR, "gdc timeout\n");
gdc_stop(gdc_cmd);
mutex_unlock(&fh->gdev->d_mutext);
#if 0
if (gs_ex->output_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF)
gdc_buffer_cache_flush(gs_ex->output_buffer.shared_fd);
#endif
if (gs_ex->input_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF) {
gdc_dma_buffer_unmap(&fh->dma_cfg.input_cfg_plane1);
if (gs_ex->input_buffer.plane_number == 2)
gdc_dma_buffer_unmap(&fh->dma_cfg.input_cfg_plane2);
}
if (gs_ex->config_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF)
gdc_dma_buffer_unmap(&fh->dma_cfg.config_cfg);
if (gs_ex->output_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF)
gdc_dma_buffer_unmap(&fh->dma_cfg.output_cfg);
return 0;
}
static long meson_gdc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long ret = -1;
size_t len;
struct mgdc_fh_s *fh = file->private_data;
struct gdc_settings gs;
struct gdc_cmd_s *gdc_cmd = &fh->gdc_cmd;
struct gdc_config_s *gc = &gdc_cmd->gdc_config;
struct gdc_buf_cfg buf_cfg;
struct page *cma_pages = NULL;
struct gdc_settings_ex gs_ex;
struct gdc_dmabuf_req_s gdc_req_buf;
struct gdc_dmabuf_exp_s gdc_exp_buf;
ion_phys_addr_t addr;
int index, dma_fd;
void __user *argp = (void __user *)arg;
switch (cmd) {
case GDC_PROCESS:
ret = copy_from_user(&gs, argp, sizeof(gs));
if (ret < 0) {
gdc_log(LOG_ERR, "copy from user failed\n");
return -EINVAL;
}
gdc_log(LOG_DEBUG, "sizeof(gs)=%zu, 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);
if (ret < 0) {
gdc_log(LOG_ERR,
"import out fd %d failed\n", gs.out_fd);
return -EINVAL;
}
memcpy(&gdc_cmd->gdc_config, &gs.gdc_config,
sizeof(struct gdc_config_s));
gdc_cmd->buffer_addr = addr;
gdc_cmd->buffer_size = len;
gdc_cmd->base_gdc = 0;
gdc_cmd->current_addr = gdc_cmd->buffer_addr;
ret = meson_ion_share_fd_to_phys(fh->ion_client,
gc->config_addr, &addr, &len);
if (ret < 0) {
gdc_log(LOG_ERR, "import config fd failed\n");
return -EINVAL;
}
gc->config_addr = addr;
ret = meson_ion_share_fd_to_phys(fh->ion_client,
gs.in_fd, &addr, &len);
if (ret < 0) {
gdc_log(LOG_ERR, "import in fd %d failed\n", gs.in_fd);
return -EINVAL;
}
ret = meson_gdc_set_input_addr(addr, gdc_cmd);
if (ret != 0) {
gdc_log(LOG_ERR, "set input addr failed\n");
return -EINVAL;
}
gdc_cmd->fh = fh;
mutex_lock(&fh->gdev->d_mutext);
ret = gdc_run(gdc_cmd);
if (ret < 0)
gdc_log(LOG_ERR, "gdc process ret = %ld\n", ret);
ret = wait_for_completion_timeout(&fh->gdev->d_com,
msecs_to_jiffies(40));
if (ret == 0)
gdc_log(LOG_ERR, "gdc timeout\n");
gdc_stop(gdc_cmd);
mutex_unlock(&fh->gdev->d_mutext);
break;
case GDC_RUN:
ret = copy_from_user(&gs, argp, sizeof(gs));
if (ret < 0)
gdc_log(LOG_ERR, "copy from user failed\n");
memcpy(&gdc_cmd->gdc_config, &gs.gdc_config,
sizeof(struct gdc_config_s));
gdc_cmd->buffer_addr = fh->o_paddr;
gdc_cmd->buffer_size = fh->o_len;
gdc_cmd->base_gdc = 0;
gdc_cmd->current_addr = gdc_cmd->buffer_addr;
gc->config_addr = fh->c_paddr;
ret = meson_gdc_set_input_addr(fh->i_paddr, gdc_cmd);
if (ret != 0) {
gdc_log(LOG_ERR, "set input addr failed\n");
return -EINVAL;
}
gdc_cmd->fh = fh;
mutex_lock(&fh->gdev->d_mutext);
meson_gdc_dma_flush(&fh->gdev->pdev->dev,
fh->i_paddr, fh->i_len);
meson_gdc_dma_flush(&fh->gdev->pdev->dev,
fh->c_paddr, fh->c_len);
ret = gdc_run(gdc_cmd);
if (ret < 0)
gdc_log(LOG_ERR, "gdc process failed ret = %ld\n", ret);
ret = wait_for_completion_timeout(&fh->gdev->d_com,
msecs_to_jiffies(40));
if (ret == 0)
gdc_log(LOG_ERR, "gdc timeout\n");
gdc_stop(gdc_cmd);
meson_gdc_cache_flush(&fh->gdev->pdev->dev,
fh->o_paddr, fh->o_len);
mutex_unlock(&fh->gdev->d_mutext);
break;
case GDC_HANDLE:
ret = copy_from_user(&gs, argp, sizeof(gs));
if (ret < 0)
gdc_log(LOG_ERR, "copy from user failed\n");
memcpy(&gdc_cmd->gdc_config, &gs.gdc_config,
sizeof(struct gdc_config_s));
gdc_cmd->buffer_addr = fh->o_paddr;
gdc_cmd->buffer_size = fh->o_len;
gdc_cmd->base_gdc = 0;
gdc_cmd->current_addr = gdc_cmd->buffer_addr;
gc->config_addr = fh->c_paddr;
gdc_cmd->fh = fh;
mutex_lock(&fh->gdev->d_mutext);
ret = meson_gdc_init_dma_addr(fh, &gs);
if (ret != 0) {
mutex_unlock(&fh->gdev->d_mutext);
gdc_log(LOG_ERR, "Failed to init dma addr");
return ret;
}
meson_gdc_dma_flush(&fh->gdev->pdev->dev,
fh->c_paddr, fh->c_len);
ret = gdc_run(gdc_cmd);
if (ret < 0)
gdc_log(LOG_ERR, "gdc process failed ret = %ld\n", ret);
ret = wait_for_completion_timeout(&fh->gdev->d_com,
msecs_to_jiffies(40));
if (ret == 0)
gdc_log(LOG_ERR, "gdc timeout\n");
gdc_stop(gdc_cmd);
meson_gdc_cache_flush(&fh->gdev->pdev->dev,
fh->o_paddr, fh->o_len);
meson_gdc_deinit_dma_addr(fh);
mutex_unlock(&fh->gdev->d_mutext);
break;
case GDC_REQUEST_BUFF:
ret = copy_from_user(&buf_cfg, argp, sizeof(buf_cfg));
if (ret < 0 || buf_cfg.type >= GDC_BUFF_TYPE_MAX) {
gdc_log(LOG_ERR, "Error user param\n");
return ret;
}
buf_cfg.len = PAGE_ALIGN(buf_cfg.len);
cma_pages = dma_alloc_from_contiguous(&fh->gdev->pdev->dev,
buf_cfg.len >> PAGE_SHIFT, 0);
if (cma_pages != NULL) {
fh->mmap_type = buf_cfg.type;
ret = meson_gdc_set_buff(fh, cma_pages, buf_cfg.len);
if (ret != 0) {
dma_release_from_contiguous(
&fh->gdev->pdev->dev,
cma_pages,
buf_cfg.len >> PAGE_SHIFT);
gdc_log(LOG_ERR, "Failed to set buff\n");
return ret;
}
} else {
gdc_log(LOG_ERR, "Failed to alloc dma buff\n");
return -ENOMEM;
}
break;
case GDC_PROCESS_EX:
ret = copy_from_user(&gs_ex, argp, sizeof(gs_ex));
if (ret < 0)
gdc_log(LOG_ERR, "copy from user failed\n");
memcpy(&gdc_cmd->gdc_config, &gs_ex.gdc_config,
sizeof(struct gdc_config_s));
ret = gdc_process_ex_info(fh, &gs_ex);
break;
case GDC_REQUEST_DMA_BUFF:
ret = copy_from_user(&gdc_req_buf, argp,
sizeof(struct gdc_dmabuf_req_s));
if (ret < 0) {
pr_err("Error user param\n");
return -EINVAL;
}
ret = gdc_buffer_alloc(&gdc_req_buf);
if (ret == 0)
ret = copy_to_user(argp, &gdc_req_buf,
sizeof(struct gdc_dmabuf_req_s));
break;
case GDC_EXP_DMA_BUFF:
ret = copy_from_user(&gdc_exp_buf, argp,
sizeof(struct gdc_dmabuf_exp_s));
if (ret < 0) {
pr_err("Error user param\n");
return -EINVAL;
}
ret = gdc_buffer_export(&gdc_exp_buf);
if (ret == 0)
ret = copy_to_user(argp, &gdc_exp_buf,
sizeof(struct gdc_dmabuf_exp_s));
break;
case GDC_FREE_DMA_BUFF:
ret = copy_from_user(&index, argp,
sizeof(int));
if (ret < 0) {
pr_err("Error user param\n");
return -EINVAL;
}
ret = gdc_buffer_free(index);
break;
case GDC_SYNC_DEVICE:
ret = copy_from_user(&dma_fd, argp,
sizeof(int));
if (ret < 0) {
pr_err("Error user param\n");
return -EINVAL;
}
gdc_buffer_dma_flush(dma_fd);
break;
case GDC_SYNC_CPU:
ret = copy_from_user(&dma_fd, argp,
sizeof(int));
if (ret < 0) {
pr_err("Error user param\n");
return -EINVAL;
}
gdc_buffer_cache_flush(dma_fd);
break;
default:
gdc_log(LOG_ERR, "unsupported cmd 0x%x\n", cmd);
return -EINVAL;
break;
}
return ret;
}
static int meson_gdc_mmap(struct file *file_p,
struct vm_area_struct *vma)
{
int ret = -1;
unsigned long buf_len = 0;
struct mgdc_fh_s *fh = file_p->private_data;
buf_len = vma->vm_end - vma->vm_start;
switch (fh->mmap_type) {
case INPUT_BUFF_TYPE:
ret = remap_pfn_range(vma, vma->vm_start,
fh->i_paddr >> PAGE_SHIFT,
buf_len, vma->vm_page_prot);
if (ret != 0)
gdc_log(LOG_ERR, "Failed to mmap input buffer\n");
break;
case OUTPUT_BUFF_TYPE:
ret = remap_pfn_range(vma, vma->vm_start,
fh->o_paddr >> PAGE_SHIFT,
buf_len, vma->vm_page_prot);
if (ret != 0)
gdc_log(LOG_ERR, "Failed to mmap input buffer\n");
break;
case CONFIG_BUFF_TYPE:
ret = remap_pfn_range(vma, vma->vm_start,
fh->c_paddr >> PAGE_SHIFT,
buf_len, vma->vm_page_prot);
if (ret != 0)
gdc_log(LOG_ERR, "Failed to mmap input buffer\n");
break;
default:
gdc_log(LOG_ERR, "Error mmap type:0x%x\n", fh->mmap_type);
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,
.mmap = meson_gdc_mmap,
};
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)
{
gdc_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)
{
gdc_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)
{
gdc_log(LOG_DEBUG, "%s, %d\n", __func__, __LINE__);
//gdc_fw_init();
return 1;
}
static DEVICE_ATTR(firmware1, 0664, firmware1_show, firmware1_store);
static ssize_t loglevel_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
len += sprintf(buf+len, "%d\n", gdc_log_level);
return len;
}
static ssize_t loglevel_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
pr_info("log_level: %d->%d\n", gdc_log_level, res);
gdc_log_level = res;
return len;
}
static DEVICE_ATTR(loglevel, 0664, loglevel_show, loglevel_store);
irqreturn_t gdc_interrupt_handler(int irq, void *param)
{
struct meson_gdc_dev_t *gdc_dev = param;
complete(&gdc_dev->d_com);
return IRQ_HANDLED;
}
static int gdc_platform_probe(struct platform_device *pdev)
{
int rc = -1;
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) {
gdc_log(LOG_ERR, "Error, no IORESOURCE_MEM DT!\n");
return -ENOMEM;
}
if (init_gdc_io(pdev->dev.of_node) != 0) {
gdc_log(LOG_ERR, "Error on mapping gdc memory!\n");
return -ENOMEM;
}
of_reserved_mem_device_init(&(pdev->dev));
gdc_dev = devm_kzalloc(&pdev->dev, sizeof(*gdc_dev),
GFP_KERNEL);
if (gdc_dev == NULL) {
gdc_log(LOG_DEBUG, "devm alloc gdc dev failed\n");
return -ENOMEM;
}
gdc_dev->pdev = pdev;
gdc_dev->misc_dev.minor = meson_gdc_dev.minor;
gdc_dev->misc_dev.name = meson_gdc_dev.name;
gdc_dev->misc_dev.fops = meson_gdc_dev.fops;
spin_lock_init(&gdc_dev->slock);
gdc_dev->irq = platform_get_irq(pdev, 0);
if (gdc_dev->irq < 0) {
gdc_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);
gdc_log(LOG_DEBUG, "pd_cntl=%x\n", reg_value);
reg_value = reg_value & (~(3<<18));
gdc_log(LOG_DEBUG, "pd_cntl=%x\n", reg_value);
iowrite32(reg_value, pd_cntl);
#endif
mutex_init(&gdc_dev->d_mutext);
init_completion(&gdc_dev->d_com);
rc = devm_request_irq(&pdev->dev, gdc_dev->irq,
gdc_interrupt_handler,
IRQF_SHARED, "gdc", gdc_dev);
if (rc != 0)
gdc_log(LOG_ERR, "cannot create irq func gdc\n");
gdc_manager.buffer = gdc_dma_buffer_create();
gdc_manager.gdc_dev = gdc_dev;
rc = misc_register(&gdc_dev->misc_dev);
if (rc < 0) {
dev_err(&pdev->dev,
"misc_register() for minor %d failed\n",
gdc_dev->misc_dev.minor);
}
device_create_file(gdc_dev->misc_dev.this_device,
&dev_attr_gdc_reg);
device_create_file(gdc_dev->misc_dev.this_device,
&dev_attr_firmware1);
device_create_file(gdc_dev->misc_dev.this_device,
&dev_attr_loglevel);
platform_set_drvdata(pdev, gdc_dev);
return rc;
}
static int gdc_platform_remove(struct platform_device *pdev)
{
device_remove_file(meson_gdc_dev.this_device,
&dev_attr_gdc_reg);
device_remove_file(meson_gdc_dev.this_device,
&dev_attr_firmware1);
device_remove_file(meson_gdc_dev.this_device,
&dev_attr_loglevel);
gdc_dma_buffer_destroy(gdc_manager.buffer);
gdc_manager.gdc_dev = NULL;
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");