copy_from_user为什么会休眠

本文探讨了在__copy_frome_user函数中使用内嵌汇编进行内存拷贝时可能遇到的问题,如页错误(page fault)。当访问内存不正确时会触发页错误,并进一步调用find_vma等带锁函数进行处理,甚至导致进程挂起或调用kmalloc分配内存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 在__copy_frome_user内嵌汇编来拷贝

内存访问不对会do_page_fault ,  这时会find_vma 之类的有锁的, 就会睡眠


kmalloc 同理
/* * SEMIDRIVE Copyright Statement * Copyright (c) SEMIDRIVE. All rights reserved * This software and all rights therein are owned by SEMIDRIVE, * and are protected by copyright law and other relevant laws, regulations and protection. * Without SEMIDRIVE’s prior written consent and /or related rights, * please do not use this software or any potion thereof in any form or by any means. * You may not reproduce, modify or distribute this software except in compliance with the License. * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OF ANY KIND, either express or implied. * You should have received a copy of the License along with this program. * If not, see <http://www.semidrive.com/licenses/>. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/component.h> #include <linux/fs.h> #include <linux/dma-buf.h> #include <linux/interrupt.h> #include <linux/iommu.h> #include <linux/pm_runtime.h> #include <linux/bitops.h> #include <linux/uaccess.h> #include <linux/list.h> #include <linux/string.h> #include <linux/bug.h> #include <linux/errno.h> #include <asm/current.h> #ifdef CONFIG_OF #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_device.h> #include <linux/of_platform.h> #else #include <linux/slab.h> #include <linux/io.h> #include <linux/irq.h> #endif #include "sdrv_g2d.h" #include "g2d_common.h" static DEFINE_MUTEX(m_init); extern const struct g2d_ops g2d_normal_ops; extern const struct g2d_ops g2d_lite_ops; extern struct ops_entry spipe_g2d_entry; extern struct ops_entry gpipe_mid_g2d_entry; extern struct ops_entry gpipe_high_g2d_entry; extern int g2d_dump_registers(struct sdrv_g2d *dev); extern int g2d_post_config(struct sdrv_g2d *dev, struct g2d_input *ins); extern int g2d_fastcopy_set(struct sdrv_g2d *dev, addr_t iaddr, u32 width, u32 height, u32 istride, addr_t oaddr, u32 ostride); extern int g2d_fill_rect(struct sdrv_g2d *dev, struct g2d_bg_cfg *bgcfg, struct g2d_output_cfg *output); extern int g2d_set_coefficients_table(struct sdrv_g2d *gd, struct g2d_coeff_table *table); extern struct attribute *sdrv_g2d_attrs[]; static const struct attribute_group *sdrv_g2d_groups[]; ATTRIBUTE_GROUPS(sdrv_g2d); static int wait_timeout = 500; module_param(wait_timeout, int, 0660); MODULE_PARM_DESC(wait_timeout, "wait timeout (ms)"); static int dump_register_g2d = 0; module_param(dump_register_g2d, int, 0660); MODULE_PARM_DESC(dump_register_g2d, "dump register g2d 0:off 1:on"); int debug_g2d = 0; EXPORT_SYMBOL(debug_g2d); module_param(debug_g2d, int, 0660); MODULE_PARM_DESC(debug_g2d, "debug g2d 0:off 1:on"); static char *version = KO_VERSION; module_param(version, charp, S_IRUGO); LIST_HEAD(g2d_pipe_list_head); int g2d_major = 227; int g2d_minor = -1; static struct sdrv_g2d *g_g2d[G2D_NR_DEVS]; const char *PIPE_TYPE_STRING[] = { GP_ECHO_NAME, GP_MID_NAME, GP_HIGH_NAME, SPIPE_NAME }; struct sdrv_g2d_data g2d_data[] = { {.version = "g2dlite-r0p0", .ops = &g2d_lite_ops}, {.version = "g2d-r0p1", .ops = &g2d_normal_ops}, {}, }; static void dump_input(struct g2d_input *input) { struct g2d_output_cfg *output = &input->output; struct g2d_layer *layer; struct g2d_bg_cfg *bg = &input->bg_layer; int i = 0; if (bg->en) { G2D_ERROR("[dump bg layer] en:%d, color:0x%x, g_alpha:0x%x, zorder:%d, bpa:0x%x, \ astride:%d, rect(%d, %d, %d, %d), pd_type:%d, fd:%d \n", bg->en, bg->color, bg->g_alpha, bg->zorder, bg->bpa, bg->astride, bg->x, bg->y, bg->width, bg->height, bg->pd_type, bg->abufs.fd); } for (i = 0; i < input->layer_num; i++) { layer = &input->layer[i]; G2D_ERROR("[dumplayer] index = %d, *ENABLE = %d*, format: %c%c%c%c source (%d, %d, %d, %d) => dest (%d, %d, %d, %d)\n", layer->index, layer->enable, layer->format & 0xff, (layer->format >> 8) & 0xff, (layer->format >> 16) & 0xff, (layer->format >> 24) & 0xff, layer->src_x, layer->src_y, layer->src_w, layer->src_h, layer->dst_x, layer->dst_y, layer->dst_w, layer->dst_h); } G2D_ERROR("[dump output]: w,h(%d,%d) format:%c%c%c%c rota:%d nplanes:%d\n", output->width, output->height, output->fmt & 0xff, (output->fmt >> 8) & 0xff, (output->fmt >> 16) & 0xff, (output->fmt >> 24) & 0xff, output->rotation, output->nplanes); return; } struct sdrv_g2d *get_g2d_by_id(int id) { return g_g2d[id]; } int g2d_ops_register(struct ops_entry *entry, struct list_head *head) { struct ops_list *list; list = kzalloc(sizeof(struct ops_list), GFP_KERNEL); if (!list) return -ENOMEM; list->entry = entry; list_add(&list->head, head); return 0; } void *g2d_ops_attach(const char *str, struct list_head *head) { struct ops_list *list; const char *ver; list_for_each_entry(list, head, head) { ver = list->entry->ver; if (!strcmp(str, ver)) return list->entry->ops; } G2D_ERROR("attach disp ops %s failed\n", str); return NULL; } irqreturn_t sdrv_g2d_irq_handler(int irq, void *data) { struct sdrv_g2d *gd = data; uint32_t val; if (!gd->du_inited) { G2D_ERROR("g2d du_inited does not init\n"); return IRQ_HANDLED; } val = gd->ops->irq_handler(gd); if (val & G2D_INT_MASK_FRM_DONE) { G2D_DBG("frame done\n"); gd->frame_done = true; wake_up(&gd->wq); } return IRQ_HANDLED; } int g2d_choose_pipe(struct sdrv_g2d *gd, int hwid, int type, uint32_t offset) { struct g2d_pipe *p = NULL; p = devm_kzalloc(&gd->pdev->dev, sizeof(struct g2d_pipe), GFP_KERNEL); if (!p) return -ENOMEM; p->type = type; p->name = PIPE_TYPE_STRING[type]; p->ops = (struct pipe_operation*)g2d_pipe_ops_attach(p->name); if (!p->ops) { G2D_ERROR("error ops attached\n"); return -EINVAL; } p->regs = gd->regs + (ulong)offset; p->iomem_regs = gd->iomem_regs + (ulong)offset; p->reg_offset = offset; p->id = hwid; p->gd = gd; gd->pipes[gd->num_pipe] = p; gd->num_pipe++; p->ops->init(p); G2D_DBG("pipe %d name %s registered\n", p->id, p->name); return 0; } #ifdef CONFIG_OF int sdrv_g2d_init(struct sdrv_g2d *gd, struct device_node *np) { int i, ret; int irq_num; struct resource res; const char *str; const struct sdrv_g2d_data *data; static int g2d_cnt = 0; if (!np || !gd) return -ENODEV; if(!of_device_is_available(np)) { G2D_ERROR("OF node %s not available or match\n", np->name); return -ENODEV; } if (!of_property_read_string(np, "sdrv,ip", &str)) { gd->name = str; } else { G2D_ERROR("sdrv,ip can not found\n"); return -ENODEV; } if (of_address_to_resource(np, 0, &res)) { G2D_ERROR("parse dt base address failed\n"); return -ENODEV; } G2D_INFO("got %s res 0x%lx\n", gd->name, (unsigned long)res.start); gd->regs = (void *)res.start; gd->iomem_regs = devm_ioremap_nocache(&gd->pdev->dev, res.start, resource_size(&res)); if(IS_ERR(gd->iomem_regs)) { G2D_ERROR("Cannot find g2d regs 001\n"); return PTR_ERR(gd->regs); } irq_num = irq_of_parse_and_map(np, 0); if (!irq_num) { G2D_ERROR("error: g2d parse irq num failed\n"); return -EINVAL; } G2D_INFO("g2d irq_num = %d\n", irq_num); data = of_device_get_match_data(&gd->pdev->dev); for (i = 0; i < 3; i++) { if (!strcmp(gd->name, data[i].version)) { gd->ops = data[i].ops; G2D_INFO("%s ops[%d] attached\n", gd->name, i); break; } } if (gd->ops == NULL) { G2D_ERROR("core ops attach failed, have checked %d times\n", i); return -1; } gd->num_pipe = 0; // g2d init gd->ops->init(gd); gd->irq = irq_num; gd->cap.num_pipe = gd->num_pipe; for (i = 0; i < gd->num_pipe; i++) { memcpy(&gd->cap.pipe_caps[i], gd->pipes[i]->cap, sizeof(struct g2d_pipe_capability)); } gd->id = g2d_cnt; irq_set_status_flags(gd->irq, IRQ_NOAUTOEN); ret = devm_request_irq(&gd->pdev->dev, gd->irq, sdrv_g2d_irq_handler, 0, dev_name(&gd->pdev->dev), gd); //IRQF_SHARED if(ret) { G2D_ERROR("Failed to request DC IRQ: %d\n", gd->irq); return -ENODEV; } //wait queue init_waitqueue_head(&gd->wq); gd->frame_done = false; g2d_cnt++; return 0; } #else int sdrv_g2d_init(struct sdrv_g2d *gd, struct platform_device *pdev) { int i, ret; int irq_num; struct device *dev = &pdev->dev; struct resource *res; const char *str; const struct sdrv_g2d_data *data; struct g2d_platform_data *pdata; static int g2d_cnt = 0; if (!pdev || !gd) return -ENODEV; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); gd->regs = (void *)res->start; gd->iomem_regs = devm_ioremap_nocache(&gd->pdev->dev, res->start, resource_size(res)); if(IS_ERR(gd->iomem_regs)) { G2D_ERROR("Cannot find g2d regs 001\n"); return PTR_ERR(gd->regs); } pdata = (struct g2d_platform_data *)platform_get_drvdata(pdev); gd->name = "g2d-r0p1"; if (!gd->name) { G2D_ERROR("sdrv,ip can not found\n"); return -ENODEV; } G2D_INFO("got %s res 0x%lx\n", gd->name, (unsigned long)gd->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); irq_num = (int) res->start; if (!irq_num) { G2D_ERROR("error: g2d parse irq num failed\n"); return -EINVAL; } G2D_INFO("g2d irq_num = %d\n", irq_num); data = g2d_data; for (i = 0; i < 16; i++) { if (!strcmp(gd->name, data[i].version)) { gd->ops = data[i].ops; G2D_DBG("%s ops[%d] attached\n", gd->name, i); break; } } if (gd->ops == NULL) { G2D_ERROR("core ops attach failed, have checked %d times\n", i); return -1; } gd->num_pipe = 0; // g2d init gd->ops->init(gd); gd->irq = irq_num; gd->cap.num_pipe = gd->num_pipe; for (i = 0; i < gd->num_pipe; i++) { memcpy(&gd->cap.pipe_caps[i], gd->pipes[i]->cap, sizeof(struct g2d_pipe_capability)); } gd->id = g2d_cnt; irq_set_status_flags(gd->irq, IRQ_NOAUTOEN); ret = devm_request_irq(&gd->pdev->dev, gd->irq, sdrv_g2d_irq_handler, 0, dev_name(&gd->pdev->dev), gd); //IRQF_SHARED if(ret) { G2D_ERROR("Failed to request DC IRQ: %d\n", gd->irq); return -ENODEV; } //wait queue init_waitqueue_head(&gd->wq); gd->frame_done = false; g2d_cnt++; return 0; } #endif static void sdrv_g2d_unit(struct sdrv_g2d *gd) { if (!gd) return; // if (gd->ops->uninit) // gd->ops->uninit(gd); } static int sdrv_g2d_open(struct inode *node, struct file *file) { int i; struct sdrv_g2d *gd = NULL; int num = MINOR(node->i_rdev); if (num < 0) return -ENODEV; for (i = 0; i < G2D_NR_DEVS; i++){ gd = get_g2d_by_id(i); if (gd->mdev.minor == num) break; } file->private_data = gd; G2D_DBG("open node %s\n", gd->name); return 0; } static int sdrv_init_iommu(struct sdrv_g2d *gd) { struct device *dev = &gd->pdev->dev; struct device_node *iommu = NULL; struct property *prop = NULL; struct iommu_domain_geometry *geometry; u64 start, end; int ret = 0; gd->iommu_enable = false; iommu = of_parse_phandle(dev->of_node, "iommus", 0); if(!iommu) { G2D_DBG("iommu not specified\n"); return ret; } if (!of_device_is_available(iommu)) { G2D_DBG("smmu disabled\n"); return ret; } prop = of_find_property(dev->of_node, "smmu", NULL); if(!prop) { G2D_DBG("smmu bypassed\n"); return ret; } gd->domain = iommu_get_domain_for_dev(dev); if(!gd->domain) { ret = -ENOMEM; goto err_free_mm;; } geometry = &gd->domain->geometry; start = geometry->aperture_start; end = GENMASK(37, 0);// 38 bits address for KUNLUN G2D rdma G2D_DBG("IOMMU context initialized: %#llx - %#llx\n", start, end); gd->iommu_enable = true; of_node_put(iommu); return ret; err_free_mm: of_node_put(iommu); return ret; } static void sdrv_iommu_cleanup(struct sdrv_g2d *gd) { if(!gd->iommu_enable) return; iommu_domain_free(gd->domain); } static unsigned long _get_contiguous_size(struct sg_table *sgt) { struct scatterlist *s; dma_addr_t expected = sg_dma_address(sgt->sgl); unsigned int i; unsigned long size = 0; for_each_sg(sgt->sgl, s, sgt->nents, i) { if (sg_dma_address(s) != expected) break; expected = sg_dma_address(s) + sg_dma_len(s); size += sg_dma_len(s); } return size; } static int g2d_dmabuf_import(struct sdrv_g2d *gd, struct g2d_buf *buf) { struct dma_buf_attachment *attach; struct sg_table *sgt; struct dma_buf *dmabuf; int ret = 0; if (buf->fd < 0) { G2D_ERROR("dmabuf handle invalid: %d\n", buf->fd); return -EINVAL; } buf->vaddr = (unsigned long)NULL; dmabuf = dma_buf_get(buf->fd); if (IS_ERR_OR_NULL(dmabuf)) { G2D_ERROR("g2d get dmabuf err from buf fd %d\n", buf->fd); return PTR_ERR(dmabuf); } attach = dma_buf_attach(dmabuf, &gd->pdev->dev); if (IS_ERR(attach)) { G2D_ERROR("dma buf attach devices faild\n"); goto out_put; } sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); G2D_ERROR("Error getting dmabuf scatterlist: errno %ld\n", PTR_ERR(sgt)); goto fail_detach; } buf->attach = attach; buf->size = _get_contiguous_size(sgt); buf->dma_addr = sg_dma_address(sgt->sgl); buf->sgt = sgt; buf->vaddr = (unsigned long)NULL; G2D_DBG("buf->size = 0x%llx \n", buf->size); if (!buf->size) { G2D_ERROR("dma buf map attachment faild, buf->size = %lld \n", buf->size); ret = -EINVAL; goto fail_unmap; } goto out_put; fail_unmap: dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); fail_detach: dma_buf_detach(dmabuf, attach); out_put: dma_buf_put(dmabuf); return ret; } static void g2d_dmabuf_release(struct sdrv_g2d *gd, struct g2d_buf *buf) { struct sg_table *sgt = buf->sgt; struct dma_buf *dmabuf; if (IS_ERR_OR_NULL(sgt)) { G2D_ERROR("dmabuf buffer is already unpinned \n"); return; } if (IS_ERR_OR_NULL(buf->attach)) { G2D_ERROR("trying to unpin a not attached buffer\n"); return; } dmabuf = dma_buf_get(buf->fd); if (IS_ERR_OR_NULL(dmabuf)) { G2D_ERROR("invalid dmabuf from dma_buf_get: %d", buf->fd); return; } G2D_DBG("buf->vaddr = 0x%ld\n", (unsigned long)buf->vaddr); if (buf->vaddr) { dma_buf_vunmap(dmabuf, (void *)buf->vaddr); buf->vaddr = (unsigned long)NULL; } dma_buf_unmap_attachment(buf->attach, sgt, 0); buf->dma_addr = 0; buf->sgt = NULL; dma_buf_detach(dmabuf, buf->attach); dma_buf_put(dmabuf); } static int g2d_alph_layer_mmap(struct sdrv_g2d *gd, struct g2d_bg_cfg *bgcfg) { int ret = 0; struct g2d_buf *buf = &bgcfg->abufs; if (buf->fd > 0) { ret = g2d_dmabuf_import(gd, buf); if (ret) { G2D_ERROR("g2d alph layer mmap faild \n"); return ret; } bgcfg->aaddr = buf->dma_addr; G2D_DBG("alph layer used, fd is valid: fd = %d , phy addr = 0x%llx\n", buf->fd, bgcfg->aaddr); } else { G2D_DBG("alph layer used, fd is invalid, aaddr = 0x%llx \n", bgcfg->aaddr); } return ret; } static int g2d_layer_mmap(struct sdrv_g2d *gd, struct g2d_layer *layer) { int ret, i, j; struct g2d_buf *buf = &layer->bufs[0]; uint32_t tmp_addr_h; uint32_t tmp_addr_l; if (buf->fd <= 0) { G2D_ERROR("input layer buf fd invaild, fd(%d) <= 0\n", buf->fd); return -EINVAL; } ret = g2d_dmabuf_import(gd, buf); if (ret) { G2D_ERROR("g2d input layer mmap faild \n"); return ret; } G2D_DBG("layer->nplanes = %d\n", layer->nplanes); for (i = 0; i < layer->nplanes; i++) { unsigned long addr = buf->dma_addr + layer->offsets[i]; layer->addr_l[i] = get_l_addr(addr); layer->addr_h[i] = get_h_addr(addr); G2D_DBG("layer[%d] addr_l[%d] = 0x%x addr_h[%d] = 0x%x\n", layer->index, i, layer->addr_l[i], i, layer->addr_h[i]); } if(layer->format == DRM_FORMAT_BGR888_PLANE) { if (layer->nplanes != 3) { G2D_ERROR("format set : DRM_FORMAT_BGR888_PLANE, but nplanes(%d) != 3 \n", layer->nplanes); return -1; } tmp_addr_l = layer->addr_l[0]; tmp_addr_h = layer->addr_h[0]; layer->addr_l[0] = layer->addr_l[2]; layer->addr_h[0] = layer->addr_h[2]; layer->addr_l[2] = tmp_addr_l; layer->addr_h[2] = tmp_addr_h; for (j = 0; j < layer->nplanes; j++) { G2D_DBG("layer[%d] addr_l[%d] = 0x%x addr_h[%d] = 0x%x\n", layer->index, j, layer->addr_l[j], j, layer->addr_h[j]); } } return 0; } int g2d_output_layer_mmap(struct sdrv_g2d *gd, struct g2d_output_cfg *layer) { int ret; int j; uint64_t tmp_addr; struct g2d_buf *buf = &layer->bufs[0]; if (buf->fd <= 0) { G2D_ERROR("output layer buf fd invaild, fd(%d) <= 0\n", buf->fd); return -EINVAL; } ret = g2d_dmabuf_import(gd, buf); if (ret) { G2D_ERROR("g2d output layer mmap faild \n"); return ret; } for (j = 0; j < layer->nplanes; j++) { layer->addr[j] = buf->dma_addr + layer->offsets[j]; G2D_DBG("layer->addr[%d] = 0x%llx \n", j, layer->addr[j]); } if(layer->fmt == DRM_FORMAT_BGR888_PLANE) { if (layer->nplanes != 3) { G2D_ERROR("fmt set : DRM_FORMAT_BGR888_PLANE, but nplanes(%d) != 3 \n", layer->nplanes); return -1; } tmp_addr = layer->addr[0]; layer->addr[0] = layer->addr[2]; layer->addr[2] = tmp_addr; for (j = 0; j < layer->nplanes; j++) { G2D_DBG("fmt == DRM_FORMAT_BGR888_PLANE : layer->addr[%d] = 0x%llx \n", j, layer->addr[j]); } } return 0; } void g2d_alph_layer_unmap(struct sdrv_g2d *gd, struct g2d_bg_cfg *bgcfg) { struct g2d_buf *buf = &bgcfg->abufs; G2D_DBG("g2d dmabuf:%d\n", buf->fd); if (buf->fd <= 0) return; g2d_dmabuf_release(gd, buf); } void g2d_layer_unmap(struct sdrv_g2d *gd, struct g2d_layer *layer) { struct g2d_buf *buf = &layer->bufs[0]; G2D_DBG("g2d dmabuf:%d\n", buf->fd); if (buf->fd <= 0) return; g2d_dmabuf_release(gd, buf); } void g2d_output_layer_unmap(struct sdrv_g2d *gd, struct g2d_output_cfg *layer) { struct g2d_buf *buf = &layer->bufs[0]; G2D_DBG("g2d dmabuf:%d\n", buf->fd); if (buf->fd <= 0) return; g2d_dmabuf_release(gd, buf); } static int g2d_ioctl_begin(struct sdrv_g2d *gd, struct g2d_input *input) { int i; int ret; set_user_nice(current, -12); /*bg layer*/ if (input->bg_layer.en) { ret = g2d_alph_layer_mmap(gd, &input->bg_layer); if (ret) { return ret; } } /*input layer*/ for (i = 0; i < input->layer_num; i++) { struct g2d_layer *l = &input->layer[i]; if (!l->enable) continue; ret = g2d_layer_mmap(gd, l); if (ret) { return ret; } } /*output layer*/ ret = g2d_output_layer_mmap(gd, &input->output); if (ret) { return ret; } return 0; } static void g2d_ioctl_finish(struct sdrv_g2d *gd, struct g2d_input *input) { int i; /*bg layer*/ if (input->bg_layer.en) { g2d_alph_layer_unmap(gd, &input->bg_layer); } /*input layer*/ for (i = 0; i < input->layer_num; i++) { struct g2d_layer *l = &input->layer[i]; if (!l->enable) continue; g2d_layer_unmap(gd, l); } /*output layer*/ g2d_output_layer_unmap(gd, &input->output); } static int g2d_wait(struct sdrv_g2d *gd) { int status = 0; int rc; //g2d_dump_registers(gd); /* wait for stop done interrupt wait_event_timeout */ rc = wait_event_timeout(gd->wq, (gd->frame_done == true), msecs_to_jiffies(wait_timeout)); gd->frame_done = false; if (!rc) { status = -1; G2D_ERROR("g2d operation wait timeout %d\n", wait_timeout); g2d_dump_registers(gd); } else { if (dump_register_g2d == 1) { g2d_dump_registers(gd); } G2D_DBG("wait time %d\n", rc); } if (gd->ops->reset) gd->ops->reset(gd); return status; } static int g2d_fill_rect_ioctl(struct sdrv_g2d *gd, struct g2d_input *input) { int ret; ret = g2d_fill_rect(gd, &input->bg_layer, &input->output); if (ret < 0) { G2D_ERROR("g2d fill rect set register err \n"); goto OUT; } ret = g2d_wait(gd); OUT: if (ret < 0) dump_input(input); return ret; } static int g2d_fastcopy_dmabuf(struct sdrv_g2d *gd, struct g2d_input *input) { int ret = -1; addr_t iaddr, oaddr; struct g2d_output_cfg *out_layer = &input->output; struct g2d_bg_cfg *bg_layer = &input->bg_layer; struct g2d_buf *buf; if (!bg_layer->en) { G2D_ERROR("bg_layer en is %d, fast copy cannot be used\n", bg_layer->en); return ret; } iaddr = bg_layer->aaddr; buf = &out_layer->bufs[0]; oaddr = buf->dma_addr + out_layer->offsets[0]; if (iaddr % 4) { G2D_ERROR("The phy-addr(0x%lx) of the input needs to be 4-byte aligned\n", iaddr); return ret; } if (oaddr % 4) { G2D_ERROR("The phy-addr(0x%lx) of the output needs to be 4-byte aligned\n", oaddr); return ret; } if ((iaddr <= 0) || (oaddr <= 0)) { G2D_ERROR("input iaddr(0x%lx) or oaddr(0x%lx) = null\n", iaddr, oaddr); return ret; } ret = g2d_fastcopy_set(gd, iaddr, out_layer->width, out_layer->height, bg_layer->astride, oaddr, out_layer->stride[0]); if (ret < 0) { G2D_ERROR("g2d_fastcopy set register err \n"); goto OUT; } ret = g2d_wait(gd); OUT: if (ret < 0) dump_input(input); return ret; } static int sdrv_g2d_post_config(struct sdrv_g2d *gd, struct g2d_input *input) { int ret = 0; ret = g2d_post_config(gd, input); if(ret < 0) goto OUT; ret = g2d_wait(gd); OUT: if (ret < 0) dump_input(input); return ret; } static int sdrv_g2d_tasks(struct sdrv_g2d *gd, unsigned int cmd, struct g2d_input *input) { int ret; mutex_lock(&gd->m_lock); if (gd->monitor.is_monitor) gd->monitor.g2d_on_task = true; if (input->tables.set_tables) {//set filter tables g2d_set_coefficients_table(gd, &input->tables); } switch (cmd) { case G2D_IOCTL_POST_CONFIG: ret = sdrv_g2d_post_config(gd, input); G2D_DBG(" G2D_IOCTL_POST_CONFIG ret = %d\n", ret); break; case G2D_IOCTL_FAST_COPY: ret = g2d_fastcopy_dmabuf(gd, input); G2D_DBG("G2D_IOCTL_FAST_COPY end ret = %d\n", ret); break; case G2D_IOCTL_FILL_RECT: ret = g2d_fill_rect_ioctl(gd, input); G2D_DBG("G2D_IOCTL_FILL_RECT end ret = %d\n", ret); break; default: G2D_ERROR("Invalid ioctl cmd: 0x%x\n", cmd); ret = -EINVAL; break; } if (input->tables.set_tables) {//reset filter tables input->tables.set_tables = false; g2d_set_coefficients_table(gd, &input->tables); } if (gd->monitor.is_monitor) gd->monitor.g2d_on_task = false; mutex_unlock(&gd->m_lock); return ret; } void sdrv_dpc_to_g2d_layer(struct dpc_layer *int_layer, struct g2d_layer *out_layer) { out_layer->index = int_layer->index; //plane index out_layer->enable = int_layer->enable; out_layer->nplanes = int_layer->nplanes; out_layer->src_x = int_layer->src_x; out_layer->src_y = int_layer->src_y; out_layer->src_w = int_layer->src_w; out_layer->src_h = int_layer->src_h; out_layer->dst_x = int_layer->dst_x; out_layer->dst_y = int_layer->dst_y; out_layer->dst_w = int_layer->dst_w; out_layer->dst_h = int_layer->dst_h; out_layer->format = int_layer->format; out_layer->alpha = int_layer->alpha; out_layer->blend_mode = int_layer->blend_mode; out_layer->rotation = int_layer->rotation; out_layer->zpos = int_layer->zpos; out_layer->xfbc = int_layer->xfbc; out_layer->modifier = int_layer->modifier; out_layer->width = int_layer->width; out_layer->height = int_layer->height; memcpy(out_layer->addr_l, int_layer->addr_l, sizeof(out_layer->addr_l)); memcpy(out_layer->addr_h, int_layer->addr_h, sizeof(out_layer->addr_h)); memcpy(out_layer->pitch, int_layer->pitch, sizeof(out_layer->pitch)); memcpy(&out_layer->comp, &int_layer->comp, sizeof(struct pix_g2dcomp)); memcpy(&out_layer->ctx, &int_layer->ctx, sizeof(struct tile_ctx)); } int sdrv_g2d_convert_format(struct dpc_layer *layer, uint32_t g2d_out_format) { int ret = 0, i = 0; struct sdrv_g2d *gd = g_g2d[0]; struct g2d_input *input = NULL; uint32_t size = 0; static dma_addr_t paddr[2]; static void *vaddr[2]; static uint8_t index = 0; if (!gd) { G2D_ERROR("g2d hasn't exist\n"); return -ENODEV; } input = kzalloc(sizeof(struct g2d_input), GFP_KERNEL); if (!input) { G2D_ERROR("alloc input error\n"); return -ENOMEM; } size = layer->src_w * layer->src_h * 2; size = round_up(size, PAGE_SIZE); if (!vaddr[0]) { for (i = 0; i < 2; i++) { vaddr[i] = dma_alloc_wc(&gd->pdev->dev, size, &paddr[i], GFP_KERNEL | __GFP_NOWARN); if(!vaddr[i]) { G2D_ERROR("failed to allocate buffer of size %u\n", size); goto alloc_dma_err; } pr_info("dma addr[%d]:0x%llx vaddr[%d]:0x%p\n", i ,paddr[i], i, vaddr[i]); } } input->layer_num = 1; memcpy(&input->layer[0], layer, sizeof(struct g2d_layer)); sdrv_dpc_to_g2d_layer(layer, &input->layer[0]); pr_debug("format:%x, w:%d, h:%d s:%d al:%x\n", layer->format, layer->src_w, layer->src_h, layer->pitch[0], layer->addr_l[0]); input->output.width = layer->dst_w; input->output.height = layer->dst_h; input->output.stride[0] = layer->dst_w * 2; input->output.fmt = g2d_out_format; input->output.nplanes = 1; input->output.addr[0] = paddr[index]; pr_debug("o format:%x, w:%d, h:%d s:%d a:%llx\n", input->output.fmt, input->output.width, input->output.height, input->output.stride[0], input->output.addr[0]); mutex_lock(&gd->m_lock); ret = sdrv_g2d_post_config(gd, input); if (ret) { mutex_unlock(&gd->m_lock); goto out; } mutex_unlock(&gd->m_lock); layer->addr_l[0] = get_l_addr(input->output.addr[0]); layer->addr_h[0] = get_h_addr(input->output.addr[0]); layer->src_h = input->output.height; layer->src_w = input->output.width; layer->dst_h = input->output.height; layer->dst_w = input->output.width; layer->pitch[0] = input->output.stride[0]; index ++; if (index >= 2) index = 0; out: kfree(input); return ret; alloc_dma_err: while (i) { dma_free_wc(&gd->pdev->dev, size, vaddr[i], paddr[i]); i--; } kfree(input); return -ENOMEM; } EXPORT_SYMBOL(sdrv_g2d_convert_format); static int sdrv_g2d_func_work(struct sdrv_g2d *gd, unsigned int cmd, struct g2d_input *input) { int ret; if (!gd || !input) { G2D_ERROR("dev or input isn't inited.[dev:%p, ins:%p]\n", gd, input); return -EINVAL; } if ((input->output.height <= 0) || (input->output.width <= 0)) { G2D_ERROR("output input->output.height = %d, input->output.width = %d\n", input->output.height, input->output.width); return -EINVAL; } G2D_DBG("\r\n"); ret = g2d_ioctl_begin(gd, input); if (ret) { G2D_ERROR("input parameter err\n"); goto finish_out; } ret = sdrv_g2d_tasks(gd, cmd, input); finish_out: g2d_ioctl_finish(gd, input); return ret; } int sdrv_g2d_dma_copy(dma_addr_t dst, dma_addr_t src, size_t data_size) { int ret = 0; struct g2d_input *input; struct sdrv_g2d *gd = g_g2d[0]; int width, height, stride; width = 32; stride = width * 4; height = (data_size / stride) + ((data_size % stride) ? 1 : 0); G2D_DBG("data_size, width, stride, height : (%ld, %d, %d, %d)\n", data_size, width, stride, height); input = kzalloc(sizeof(struct g2d_input), GFP_ATOMIC | GFP_DMA); if (!input) { G2D_ERROR("kzalloc input failed\n"); return -EFAULT; } input->bg_layer.en = 1; input->bg_layer.width = width; input->bg_layer.height = height; input->bg_layer.astride = stride; input->bg_layer.aaddr = (uint64_t)src; input->output.bufs[0].dma_addr = (uint64_t)dst; input->output.width = width; input->output.height = height; input->output.stride[0] = stride; ret = sdrv_g2d_tasks(gd, G2D_IOCTL_FAST_COPY, input); kfree(input); return ret; } EXPORT_SYMBOL(sdrv_g2d_dma_copy); static long sdrv_g2d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = -1; int i=0, n = 0; struct sdrv_g2d *gd = file->private_data; struct g2d_input *input; struct g2d_inputx *inputx; if (_IOC_TYPE(cmd) != G2D_IOCTL_BASE) return -EINVAL; if (_IOC_NR(cmd) > 4) return -EINVAL; if (_IOC_DIR(cmd) & _IOC_READ) { ret = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); if (ret) return -EFAULT; } if (_IOC_DIR(cmd) & _IOC_WRITE) { ret = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); if (ret) return -EFAULT; } inputx = kzalloc(sizeof(struct g2d_inputx), GFP_ATOMIC | GFP_DMA); if (!inputx) { G2D_ERROR("kzalloc input failed\n"); return -EFAULT; } input = kzalloc(sizeof(struct g2d_input), GFP_ATOMIC | GFP_DMA); if (!input) { G2D_ERROR("kzalloc input failed\n"); if (inputx) kfree(inputx); return -EFAULT; } memset(inputx,0,sizeof(struct g2d_inputx)); memset(input,0,sizeof(struct g2d_input)); if (cmd == G2D_IOCTL_GET_CAPABILITIES) { ret = copy_to_user((struct g2d_capability __user *)arg, &gd->cap, sizeof(struct g2d_capability)); if (ret) { G2D_ERROR("get capabilities err \n"); ret = -EFAULT; } } else { ret = copy_from_user(inputx, (struct g2d_inputx __user *)arg, sizeof(struct g2d_inputx)); if (ret) { G2D_ERROR("copy_from_user failed\n"); ret = -EFAULT; goto unlock_out; } //for 32bit and 64 bit capibility; input->layer_num = inputx->layer_num; memcpy((void *)(&input->bg_layer),(void *)(&inputx->bg_layer),sizeof(struct g2d_bg_cfg_x)); input->bg_layer.abufs.dma_addr = input->bg_layer.cfg_buf.dma_addr; input->bg_layer.abufs.fd = input->bg_layer.cfg_buf.fd; input->bg_layer.abufs.size = input->bg_layer.cfg_buf.size; input->bg_layer.abufs.vaddr = input->bg_layer.cfg_buf.vaddr; memcpy((void *)(&input->output), (void *)(&inputx->output),sizeof(struct g2d_output_cfg_x)); for (i = 0; i < 4; i++) { input->output.bufs[i].dma_addr = input->output.out_buf[i].dma_addr; input->output.bufs[i].fd = input->output.out_buf[i].fd; input->output.bufs[i].size = input->output.out_buf[i].size; input->output.bufs[i].vaddr = input->output.out_buf[i].vaddr; } memcpy((void *)(&input->tables), (void *)(&inputx->tables),sizeof(struct g2d_coeff_table)); for (n = 0; n < G2D_LAYER_MAX_NUM;n ++) { memcpy((void *)(&input->layer[n]),(void *)(&inputx->layer[n]),sizeof(struct g2d_layer_x)); for (i = 0; i < 4; i++) { input->layer[n].bufs[i].dma_addr = input->layer[n].in_buf[i].dma_addr; input->layer[n].bufs[i].fd = input->layer[n].in_buf[i].fd; input->layer[n].bufs[i].size = input->layer[n].in_buf[i].size; input->layer[n].bufs[i].vaddr = input->layer[n].in_buf[i].vaddr; } } ret = sdrv_g2d_func_work(gd, cmd, input); } unlock_out: if (input) kfree(input); if (inputx) kfree(inputx); return (long)ret; } #if defined(CONFIG_COMPAT) static long sdrv_g2d_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return sdrv_g2d_ioctl(file, cmd, arg); } #endif /* defined(CONFIG_COMPAT) */ ssize_t sdrv_g2d_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { struct sdrv_g2d *gd = file->private_data; char str[64] = {0}; ssize_t sz = sprintf(str, "read from %s\n", gd->name); if (copy_to_user(buf, str, sz)){ G2D_ERROR("copy to user failed: %s\n", gd->name); } return sz; } static const struct file_operations g2d_fops = { .owner = THIS_MODULE, .open = sdrv_g2d_open, .read = sdrv_g2d_read, .unlocked_ioctl = sdrv_g2d_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = sdrv_g2d_compat_ioctl, #endif }; static int g2d_misc_init(struct sdrv_g2d *gd) { int ret; struct miscdevice *m = &gd->mdev;; m->minor = MISC_DYNAMIC_MINOR; m->name = kasprintf(GFP_KERNEL, "g2d%d", gd->id); m->fops = &g2d_fops; m->parent = NULL; m->groups = sdrv_g2d_groups; ret = misc_register(m); if (ret) { G2D_ERROR("failed to register miscdev\n"); return ret; } G2D_INFO("%s misc register \n", m->name); return ret; } static int sdrv_g2d_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct sdrv_g2d *gd = NULL; static int pipe_registered = 0; dma_addr_t dma_handle; int ret = 0, i; mutex_lock(&m_init); G2D_INFO("G2D BUILD VERSION : %s \n", version); // 38 bits address for KUNLUN G2D rdma,use G2D_CPU_WRITE config 38bit; use G2D_CMD_WRITE config 32bit dma_set_mask(dev, DMA_BIT_MASK(32)); dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); gd = devm_kzalloc(&pdev->dev, sizeof(struct sdrv_g2d), GFP_KERNEL); if (!gd) { G2D_ERROR("kalloc sdrv_g2d failed\n"); ret = -1; goto OUT; } gd->du_inited = false; gd->pdev = pdev; if (!pipe_registered) { pipe_registered++; g2d_pipe_ops_register(&spipe_g2d_entry); g2d_pipe_ops_register(&gpipe_high_g2d_entry); g2d_pipe_ops_register(&gpipe_mid_g2d_entry); } /*cmdfile init*/ gd->cmd_info[0].arg = (unsigned int*)dma_alloc_coherent(dev, G2D_CMDFILE_MAX_MEM * sizeof(unsigned int), &dma_handle, GFP_KERNEL); gd->dma_buf = (unsigned long)dma_handle; if (gd->cmd_info[0].arg == NULL) { G2D_ERROR("malloc cmd_info failed\n"); goto OUT; } G2D_INFO("gd->cmd_info[0].arg virtual address = 0x%lx, phy address 0x%lx,dma alloc coherent len = %ld\n", (unsigned long)gd->cmd_info[0].arg, gd->dma_buf, G2D_CMDFILE_MAX_MEM * sizeof(unsigned int)); for (i = 1 ; i < G2D_CMDFILE_MAX_NUM; i++) { gd->cmd_info[i].arg = gd->cmd_info[i - 1].arg + G2D_CMDFILE_MAX_MEM / G2D_CMDFILE_MAX_NUM; } #ifdef CONFIG_OF G2D_INFO("CONFIG_OF scope\n"); sdrv_init_iommu(gd); ret = sdrv_g2d_init(gd, dev->of_node); #else G2D_INFO("CONFIG_OF is closed\n"); ret = sdrv_g2d_init(gd, pdev); #endif if (ret) goto OUT; mutex_init(&gd->m_lock); gd->monitor.sampling_time = 5; ret = g2d_misc_init(gd); if (ret) goto OUT; else printk("%s : semidrive g2d driver registered.\n", __func__); platform_set_drvdata(pdev, gd); g_g2d[gd->id] = gd; gd->du_inited = true; enable_irq(gd->irq); ret = 0; OUT: mutex_unlock(&m_init); return ret; } static int sdrv_g2d_remove(struct platform_device *pdev) { struct sdrv_g2d *gd = platform_get_drvdata(pdev); G2D_DBG("remove g2d %s\n", gd->name); if (gd) { sdrv_iommu_cleanup(gd); sdrv_g2d_unit(gd); misc_deregister(&gd->mdev); } return 0; } #ifdef CONFIG_OF static const struct of_device_id g2d_of_table[] = { {.compatible = "semidrive,g2d", .data = g2d_data}, {.compatible = "semidrive,g2d_lite", .data = g2d_data}, {}, }; #endif static int sdrv_g2d_suspend(struct device *dev) { struct sdrv_g2d *gd = dev_get_drvdata(dev); G2D_INFO("%s start\n", __func__); gd->ops->reset(gd); G2D_INFO("gd->du_inited = %d, gd->num_pipe = %d\n", gd->du_inited, gd->num_pipe); G2D_INFO("%s end\n", __func__); return 0; } static int sdrv_g2d_resume(struct device *dev) { struct sdrv_g2d *gd = dev_get_drvdata(dev); struct g2d_pipe *p = NULL; int i; G2D_INFO("%s start\n", __func__); G2D_INFO("gd->du_inited = %d, gd->num_pipe = %d\n", gd->du_inited, gd->num_pipe); gd->ops->init(gd); for (i = 0; i < gd->num_pipe; i++) { p = gd->pipes[i]; if (p && p->ops->init) p->ops->init(p); else G2D_ERROR("p or p->ops->init is null\n"); } gd->ops->reset(gd); G2D_INFO("%s end\n", __func__); return 0; } static const struct dev_pm_ops sdrv_g2d_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(sdrv_g2d_suspend, sdrv_g2d_resume) }; static struct platform_driver g2d_driver = { .probe = sdrv_g2d_probe, .remove = sdrv_g2d_remove, .driver = { .name = "semidrive-g2d", .owner = THIS_MODULE, #ifdef CONFIG_OF .of_match_table = g2d_of_table, #endif .pm = &sdrv_g2d_pm_ops, }, }; module_platform_driver(g2d_driver); MODULE_AUTHOR("Semidrive Semiconductor"); MODULE_DESCRIPTION("Semidrive g2d"); MODULE_LICENSE("GPL"); 以上是linux内核g2d驱动文件sdrv_g2d.c #ifndef __SDRV_G2D_H__ #define __SDRV_G2D_H__ #include <linux/platform_device.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/miscdevice.h> #include <linux/list.h> #include <asm/io.h> #include <linux/iommu.h> #include <linux/wait.h> #include <uapi/drm/drm_fourcc.h> #include <uapi/drm/sdrv_g2d_cfg.h> #include "g2d_common.h" #define PR_INFO pr_info #define ERROR pr_err typedef unsigned long int addr_t; #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif extern int debug_g2d; #define G2D_INFO(fmt, args...) do {\ PR_INFO("[g2d] [%20s] " fmt, __func__, ##args);\ }while(0) #define G2D_DBG(fmt, args...) do {\ if (debug_g2d >= 1) {\ PR_INFO("[g2d] <%d> [%20s] " fmt, __LINE__, __func__, ##args);}\ }while(0) #define G2D_ERROR(fmt, args...) ERROR("[g2d] <%d> [%20s] Error: " fmt, __LINE__, __func__, ##args) #define DDBG(x) G2D_DBG(#x " -> %d\n", x) #define XDBG(x) G2D_DBG(#x " -> 0x%x\n", x) #define PDBG(x) G2D_DBG(#x " -> %p\n", x) #define ENTRY() G2D_DBG("call <%d>\n", __LINE__) #define GP_ECHO_NAME "g2d_gpipe_echo" #define GP_MID_NAME "g2d_gpipe_mid" #define GP_HIGH_NAME "g2d_gpipe_high" #define SPIPE_NAME "g2d_spipe" #define G2D_NR_DEVS 4 /*Kunlun DP layer format TILE vsize*/ enum { TILE_VSIZE_1 = 0b000, TILE_VSIZE_2 = 0b001, TILE_VSIZE_4 = 0b010, TILE_VSIZE_8 = 0b011, TILE_VSIZE_16 = 0b100, }; /*Kunlun DP layer format TILE hsize*/ enum { TILE_HSIZE_1 = 0b000, TILE_HSIZE_8 = 0b001, TILE_HSIZE_16 = 0b010, TILE_HSIZE_32 = 0b011, TILE_HSIZE_64 = 0b100, TILE_HSIZE_128 = 0b101, }; /**/ enum { FBDC_U8U8U8U8 = 0xc, FBDC_U16 = 0x9, FBDC_R5G6B5 = 0x5, FBDC_U8 = 0x0, FBDC_NV21 = 0x37, FBDC_YUV420_16PACK = 0x65 }; enum kunlun_plane_property { PLANE_PROP_ALPHA, PLANE_PROP_BLEND_MODE, PLANE_PROP_FBDC_HSIZE_Y, PLANE_PROP_FBDC_HSIZE_UV, PLANE_PROP_CAP_MASK, PLANE_PROP_MAX_NUM }; enum { DRM_MODE_BLEND_PIXEL_NONE = 0, DRM_MODE_BLEND_PREMULTI, DRM_MODE_BLEND_COVERAGE }; enum { PLANE_DISABLE, PLANE_ENABLE }; enum { PROP_PLANE_CAP_RGB = 0, PROP_PLANE_CAP_YUV, PROP_PLANE_CAP_XFBC, PROP_PLANE_CAP_YUV_FBC, PROP_PLANE_CAP_ROTATION, PROP_PLANE_CAP_SCALING, }; enum { TYPE_GP_ECHO = 0, TYPE_GP_MID, TYPE_GP_HIGH, TYPE_SPIPE }; struct g2d_pipe; struct pipe_operation { int (*init)(struct g2d_pipe *); int (*set)(struct g2d_pipe *, int , struct g2d_layer *); void (*csc_coef_set)(struct g2d_pipe *, struct g2d_coeff_table *); }; struct g2d_pipe { void __iomem *iomem_regs; void __iomem *regs; unsigned long reg_offset; int id; // the ordered id from 0 struct sdrv_g2d *gd; const char *name; int type; struct pipe_operation *ops; struct g2d_pipe_capability *cap; struct g2d_pipe *next; }; struct g2d_monitor { int is_monitor; int is_init; ktime_t timeout; struct hrtimer timer; bool g2d_on_task; int occupancy_rate; int timer_count; int valid_times; int sampling_time; }; struct sdrv_g2d { struct platform_device *pdev; struct cdev cdev; struct miscdevice mdev; void __iomem *iomem_regs; void __iomem *regs; bool iommu_enable; struct iommu_domain *domain; struct mutex m_lock; struct wait_queue_head wq; bool frame_done; int id; const char *name; int irq; int write_mode; cmdfile_info cmd_info[G2D_CMDFILE_MAX_NUM]; unsigned long dma_buf; const struct g2d_ops *ops; struct g2d_capability cap; struct g2d_pipe *pipes[PIPE_MAX]; int num_pipe; int du_inited; struct g2d_monitor monitor; }; struct g2d_ops { int (*init)(struct sdrv_g2d *); int (*enable)(struct sdrv_g2d*, int); int (*reset)(struct sdrv_g2d *); int (*mlc_set)(struct sdrv_g2d *, int , struct g2d_input *); int (*fill_rect)(struct sdrv_g2d *, struct g2d_bg_cfg *, struct g2d_output_cfg *); int (*fastcopy)(struct sdrv_g2d *, addr_t , u32 , u32 , u32 , addr_t , u32); int (*config)(struct sdrv_g2d *); int (*irq_handler)(struct sdrv_g2d *); int (*rwdma)(struct sdrv_g2d *, struct g2d_input *); void (*close_fastcopy)(struct sdrv_g2d *); int (*wpipe_set)(struct sdrv_g2d *, int, struct g2d_output_cfg *); int (*check_stroke)(struct g2d_input *); int (*scaler_coef_set)(struct sdrv_g2d *, struct g2d_coeff_table *); }; struct sdrv_g2d_data { const char *version; const struct g2d_ops* ops; }; struct ops_entry { const char *ver; void *ops; }; int g2d_get_capability(struct g2d_capability *cap); unsigned int get_compval_from_comp(struct pix_g2dcomp *comp); unsigned int get_frm_ctrl_from_comp(struct pix_g2dcomp *comp); int sdrv_wpipe_pix_comp(uint32_t format, struct pix_g2dcomp *comp); int sdrv_pix_comp(uint32_t format, struct pix_g2dcomp *comp); bool g2d_format_is_yuv(uint32_t format); int g2d_format_wpipe_bypass(uint32_t format); struct ops_list { struct list_head head; struct ops_entry *entry; }; extern struct list_head g2d_pipe_list_head; int g2d_ops_register(struct ops_entry *entry, struct list_head *head); void *g2d_ops_attach(const char *str, struct list_head *head); #define g2d_pipe_ops_register(entry) g2d_ops_register(entry, &g2d_pipe_list_head) #define g2d_pipe_ops_attach(str) g2d_ops_attach(str, &g2d_pipe_list_head) int g2d_choose_pipe(struct sdrv_g2d *gd, int hwid, int type, uint32_t offset); struct sdrv_g2d *get_g2d_by_id(int id); extern struct ops_entry gpipe_mid_g2d_entry; extern struct ops_entry gpipe_high_g2d_entry; extern struct ops_entry spipe_g2d_entry; #endif //__SDRV_G2D_H__ 以上是linux内核的g2d驱动的头文件sdrv_g2d.h #ifndef __SDRV_G2D_CFG_H #define __SDRV_G2D_CFG_H #include "sdrv_drm.h" #ifdef __YOCTO_G2D_TEST__ typedef __u8 uint8_t; typedef __u16 uint16_t; typedef __u32 uint32_t; typedef unsigned long uint64_t; #endif #define G2D_LAYER_MAX_NUM 6 #ifndef G2DLITE_API_USE typedef enum { SWAP_A_RGB = 0b0000, SWAP_A_RBG = 0b0001, SWAP_A_GBR = 0b0010, SWAP_A_GRB = 0b0011, SWAP_A_BGR = 0b0100, SWAP_A_BRG = 0b0101, SWAP_B_ARG = 0b1000, SWAP_B_AGR = 0b1001, SWAP_B_RGA = 0b1010, SWAP_B_RAG = 0b1011, SWAP_B_GRA = 0b1100, SWAP_B_GAR = 0b1101 } COMP_SWAP_MODE; typedef enum { UV_YUV444_RGB = 0b00, UV_YUV422 = 0b01, UV_YUV440 = 0b10, UV_YUV420 = 0b11 } DATA_UV_MODE; typedef enum { LINEAR_MODE = 0b000, RLE_COMPR_MODE = 0b001, GPU_RAW_TILE_MODE = 0b010, GPU_CPS_TILE_MODE = 0b011, VPU_RAW_TILE_MODE = 0b100, VPU_CPS_TILE_MODE = 0b101, VPU_RAW_TILE_988_MODE = 0b110, } DATA_MODE; typedef enum { FMT_INTERLEAVED = 0b00, FMT_MONOTONIC = 0b01, FMT_SEMI_PLANAR = 0b10, FMT_PLANAR = 0b11 } FRM_BUF_STR_FMT; typedef enum { ROT_DEFAULT = 0b000, ROT_ROT = 0b001, ROT_VFLIP = 0b010, ROT_HFLIP = 0b100 } ROT_TYPE; #endif #ifndef G2DLITE_API_USE enum { BLEND_PIXEL_NONE = 0, BLEND_PIXEL_PREMULTI, BLEND_PIXEL_COVERAGE }; typedef enum { ROTATION_TYPE_NONE = 0b000, ROTATION_TYPE_ROT_90 = 0b001, ROTATION_TYPE_HFLIP = 0b010, ROTATION_TYPE_VFLIP = 0b100, ROTATION_TYPE_ROT_180 = ROTATION_TYPE_VFLIP | ROTATION_TYPE_HFLIP, ROTATION_TYPE_ROT_270 = ROTATION_TYPE_ROT_90 | ROTATION_TYPE_VFLIP | ROTATION_TYPE_HFLIP, ROTATION_TYPE_VF_90 = ROTATION_TYPE_VFLIP | ROTATION_TYPE_ROT_90, ROTATION_TYPE_HF_90 = ROTATION_TYPE_HFLIP | ROTATION_TYPE_ROT_90, } rotation_type; #endif typedef enum { PD_NONE = 0, PD_SRC = 0x1, PD_DST = 0x2 } PD_LAYER_TYPE; struct g2d_output_cfg{ uint32_t width; uint32_t height; uint32_t fmt; uint64_t addr[4]; uint32_t stride[4]; uint32_t rotation; uint32_t nplanes; uint32_t offsets[4]; struct tile_ctx out_ctx; struct g2d_buf_info out_buf[4]; struct g2d_buf bufs[4]; }; struct g2d_bg_cfg { uint32_t en; uint32_t color; uint8_t g_alpha; uint8_t zorder; uint64_t aaddr; uint8_t bpa; uint32_t astride; uint32_t x; uint32_t y; uint32_t width; uint32_t height; PD_LAYER_TYPE pd_type; struct g2d_buf_info cfg_buf; struct g2d_buf abufs; }; struct g2d_coeff_table { int set_tables; int hcoef_set; int hcoef[33][5]; int vcoef_set; int vcoef[33][4]; int csc_coef_set; int csc_coef[15]; }; struct g2d_input{ unsigned char layer_num; struct g2d_bg_cfg bg_layer; struct g2d_layer layer[G2D_LAYER_MAX_NUM]; struct g2d_output_cfg output; struct g2d_coeff_table tables; }; struct g2d_pipe_capability { uint32_t formats[100]; int nformats; int layer_type; int rotation; int scaling; int yuv; int yuv_fbc; int xfbc; }; struct g2d_capability { int num_pipe; struct g2d_pipe_capability pipe_caps[G2D_LAYER_MAX_NUM]; }; struct g2d_layer_x { __u8 index; //plane index __u8 enable; __u8 nplanes; __u32 addr_l[4]; __u32 addr_h[4]; __u32 pitch[4]; __u32 offsets[4]; __s16 src_x; __s16 src_y; __s16 src_w; __s16 src_h; __s16 dst_x; __s16 dst_y; __u16 dst_w; __u16 dst_h; __u32 format; struct pix_g2dcomp comp; struct tile_ctx ctx; __u32 alpha; __u32 blend_mode; __u32 rotation; __u32 zpos; __u32 xfbc; __u64 modifier; __u32 width; __u32 height; struct g2d_buf_info in_buf[4]; }; struct g2d_output_cfg_x{ uint32_t width; uint32_t height; uint32_t fmt; uint64_t addr[4]; uint32_t stride[4]; uint32_t rotation; uint32_t nplanes; uint32_t offsets[4]; struct tile_ctx out_ctx; struct g2d_buf_info out_buf[4]; }; struct g2d_bg_cfg_x { uint32_t en; uint32_t color; uint8_t g_alpha; uint8_t zorder; uint64_t aaddr; uint8_t bpa; uint32_t astride; uint32_t x; uint32_t y; uint32_t width; uint32_t height; PD_LAYER_TYPE pd_type; struct g2d_buf_info cfg_buf; }; struct g2d_inputx{ unsigned char layer_num; struct g2d_bg_cfg_x bg_layer; struct g2d_layer_x layer[G2D_LAYER_MAX_NUM]; struct g2d_output_cfg_x output; struct g2d_coeff_table tables; }; #define G2D_COMMAND_BASE 0x00 #define G2D_IOCTL_BASE 'g' #define G2D_IO(nr) _IO(G2D_IOCTL_BASE,nr) #define G2D_IOR(nr,type) _IOR(G2D_IOCTL_BASE,nr,type) #define G2D_IOW(nr,type) _IOW(G2D_IOCTL_BASE,nr,type) #define G2D_IOWR(nr,type) _IOWR(G2D_IOCTL_BASE,nr,type) #define G2D_IOCTL_GET_CAPABILITIES G2D_IOWR(G2D_COMMAND_BASE + 1, struct g2d_capability) #define G2D_IOCTL_POST_CONFIG G2D_IOWR(G2D_COMMAND_BASE + 2, struct g2d_inputx) #define G2D_IOCTL_FAST_COPY G2D_IOWR(G2D_COMMAND_BASE + 3, struct g2d_inputx) #define G2D_IOCTL_FILL_RECT G2D_IOWR(G2D_COMMAND_BASE + 4, struct g2d_inputx) #endif //__SDRV_G2D_CFG_H 以上是linux内核提供给linux应用层调用的头文件。 请提供所有文件的中文详细注释,并结合lvgl 9.2.2版版本源码和/dev/g2d0设备,实现调用g2d硬件资源进行图形绘制linux应用代码。
07-25
impl VirtioDevice for VhostUserFrontend { // Override the default debug label to differentiate vhost-user devices from virtio. fn debug_label(&self) -> String { format!("vu-{}", self.device_type()) } fn keep_rds(&self) -> Vec<RawDescriptor> { Vec::new() } fn device_type(&self) -> DeviceType { self.device_type } fn queue_max_sizes(&self) -> &[u16] { &self.queue_sizes } fn features(&self) -> u64 { self.avail_features } fn ack_features(&mut self, features: u64) { let features = (features & self.avail_features) | self.acked_features; if let Err(e) = self .backend_client .lock() .set_features(features) .map_err(Error::SetFeatures) { error!("failed to enable features 0x{:x}: {}", features, e); return; } self.acked_features = features; } fn read_config(&self, offset: u64, data: &mut [u8]) { if let Some(cfg) = &self.cfg { copy_config(data, 0, cfg, offset); return; } let Ok(offset) = offset.try_into() else { error!("failed to read config: invalid config offset is given: {offset}"); return; }; let Ok(data_len) = data.len().try_into() else { error!( "failed to read config: invalid config length is given: {}", data.len() ); return; }; let (_, config) = match self.backend_client.lock().get_config( offset, data_len, VhostUserConfigFlags::WRITABLE, data, ) { Ok(x) => x, Err(e) => { error!("failed to read config: {}", Error::GetConfig(e)); return; } }; data.copy_from_slice(&config); } fn write_config(&mut self, offset: u64, data: &[u8]) { let Ok(offset) = offset.try_into() else { error!("failed to write config: invalid config offset is given: {offset}"); return; }; if let Err(e) = self .backend_client .lock() .set_config(offset, VhostUserConfigFlags::empty(), data) .map_err(Error::SetConfig) { error!("failed to write config: {}", e); } } fn activate( &mut self, mem: GuestMemory, interrupt: Interrupt, queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()> { self.set_mem_table(&mem)?; let msix_config_opt = interrupt .get_msix_config() .as_ref() .ok_or(Error::MsixConfigUnavailable)?; let msix_config = msix_config_opt.lock(); let non_msix_evt = Event::new().map_err(Error::CreateEvent)?; for (&queue_index, queue) in queues.iter() { let irqfd = msix_config .get_irqfd(queue.vector() as usize) .unwrap_or(&non_msix_evt); self.activate_vring(&mem, queue_index, queue, irqfd)?; } self.sent_queues = Some(queues); drop(msix_config); self.start_worker(interrupt, non_msix_evt); Ok(()) } fn reset(&mut self) -> anyhow::Result<()> { if let Some(sent_queues) = self.sent_queues.take() { for queue_index in sent_queues.into_keys() { let _vring_base = self .deactivate_vring(queue_index) .context("deactivate_vring failed during reset")?; } } if let Some(w) = self.worker_thread.take() { self.backend_req_handler = w.stop(); } Ok(()) } fn pci_address(&self) -> Option<PciAddress> { self.pci_address } fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> { if !self .protocol_features .contains(VhostUserProtocolFeatures::SHARED_MEMORY_REGIONS) { return None; } if let Some(r) = self.shmem_region.borrow().as_ref() { return r.clone(); } let regions = match self .backend_client .lock() .get_shared_memory_regions() .map_err(Error::ShmemRegions) { Ok(x) => x, Err(e) => { error!("Failed to get shared memory regions {}", e); return None; } }; let region = match regions.len() { 0 => None, 1 => Some(SharedMemoryRegion { id: regions[0].id, length: regions[0].length, }), n => { error!( "Failed to get shared memory regions {}", Error::TooManyShmemRegions(n) ); return None; } }; *self.shmem_region.borrow_mut() = Some(region.clone()); region } fn set_shared_memory_mapper(&mut self, mapper: Box<dyn SharedMemoryMapper>) { // Return error if backend request handler is not available. This indicates // that `VhostUserProtocolFeatures::BACKEND_REQ` is not negotiated. let Some(backend_req_handler) = self.backend_req_handler.as_mut() else { error!( "Error setting shared memory mapper {}", Error::ProtocolFeatureNotNegoiated(VhostUserProtocolFeatures::BACKEND_REQ) ); return; }; // The virtio framework will only call this if get_shared_memory_region returned a region let shmid = self .shmem_region .borrow() .clone() .flatten() .expect("missing shmid") .id; backend_req_handler .frontend_mut() .set_shared_mapper_state(mapper, shmid); } fn expose_shmem_descriptors_with_viommu(&self) -> bool { self.expose_shmem_descriptors_with_viommu } fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> { let Some(mut queues) = self.sent_queues.take() else { return Ok(None); }; for (&queue_index, queue) in queues.iter_mut() { let vring_base = self .deactivate_vring(queue_index) .context("deactivate_vring failed during sleep")?; queue.vhost_user_reclaim(vring_base); } if let Some(w) = self.worker_thread.take() { self.backend_req_handler = w.stop(); } Ok(Some(queues)) } fn virtio_wake( &mut self, // Vhost user doesn't need to pass queue_states back to the device process, since it will // already have it. queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, ) -> anyhow::Result<()> { if let Some((mem, interrupt, queues)) = queues_state { self.activate(mem, interrupt, queues)?; } Ok(()) } fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> { if !self .protocol_features .contains(VhostUserProtocolFeatures::DEVICE_STATE) { bail!("snapshot requires VHOST_USER_PROTOCOL_F_DEVICE_STATE"); } let backend_client = self.backend_client.lock(); // Send the backend an FD to write the device state to. If it gives us an FD back, then // we need to read from that instead. let (mut r, w) = new_pipe_pair()?; let backend_r = backend_client .set_device_state_fd( VhostUserTransferDirection::Save, VhostUserMigrationPhase::Stopped, &w, ) .context("failed to negotiate device state fd")?; // EOF signals end of the device state bytes, so it is important to close our copy of // the write FD before we start reading. std::mem::drop(w); // Read the device state. let mut snapshot_bytes = Vec::new(); if let Some(mut backend_r) = backend_r { backend_r.read_to_end(&mut snapshot_bytes) } else { r.read_to_end(&mut snapshot_bytes) } .context("failed to read device state")?; // Call `check_device_state` to ensure the data transfer was successful. backend_client .check_device_state() .context("failed to transfer device state")?; Ok(AnySnapshot::to_any(snapshot_bytes).map_err(Error::SliceToSerdeValue)?) } fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> { if !self .protocol_features .contains(VhostUserProtocolFeatures::DEVICE_STATE) { bail!("restore requires VHOST_USER_PROTOCOL_F_DEVICE_STATE"); } let backend_client = self.backend_client.lock(); let data_bytes: Vec<u8> = AnySnapshot::from_any(data).map_err(Error::SerdeValueToSlice)?; // Send the backend an FD to read the device state from. If it gives us an FD back, // then we need to write to that instead. let (r, w) = new_pipe_pair()?; let backend_w = backend_client .set_device_state_fd( VhostUserTransferDirection::Load, VhostUserMigrationPhase::Stopped, &r, ) .context("failed to negotiate device state fd")?; // Write the device state. { // EOF signals the end of the device state bytes, so we need to ensure the write // objects are dropped before the `check_device_state` call. Done here by moving // them into this scope. let backend_w = backend_w; let mut w = w; if let Some(mut backend_w) = backend_w { backend_w.write_all(data_bytes.as_slice()) } else { w.write_all(data_bytes.as_slice()) } .context("failed to write device state")?; } // Call `check_device_state` to ensure the data transfer was successful. backend_client .check_device_state() .context("failed to transfer device state")?; Ok(()) } }
05-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值