/* Copyright (c) 2011-2013, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * * 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 #include #include #include #include "enc-subdev.h" #include "mdp-subdev.h" #include "wfd-util.h" struct mdp_instance { struct fb_info *mdp; u32 height; u32 width; bool secure; }; static int mdp_secure(struct v4l2_subdev *sd, void *arg); int mdp_init(struct v4l2_subdev *sd, u32 val) { return 0; } int mdp_open(struct v4l2_subdev *sd, void *arg) { struct mdp_instance *inst = kzalloc(sizeof(struct mdp_instance), GFP_KERNEL); struct mdp_msg_ops *mops = arg; int rc = 0; struct fb_info *fbi = NULL; if (!inst) { WFD_MSG_ERR("Out of memory\n"); rc = -ENOMEM; goto mdp_open_fail; } else if (!mops) { WFD_MSG_ERR("Invalid arguments\n"); rc = -EINVAL; goto mdp_open_fail; } fbi = msm_fb_get_writeback_fb(); if (!fbi) { WFD_MSG_ERR("Failed to acquire mdp instance\n"); rc = -ENODEV; goto mdp_open_fail; } msm_fb_writeback_init(fbi); inst->mdp = fbi; inst->secure = mops->secure; if (mops->secure) { rc = mdp_secure(sd, inst); if (rc) { WFD_MSG_ERR("Couldn't secure MDP\n"); goto mdp_secure_fail; } } mops->cookie = inst; return 0; mdp_secure_fail: msm_fb_writeback_terminate(fbi); mdp_open_fail: kfree(inst); return rc; } int mdp_start(struct v4l2_subdev *sd, void *arg) { struct mdp_instance *inst = arg; int rc = 0; if (inst) { rc = msm_fb_writeback_start(inst->mdp); if (rc) { WFD_MSG_ERR("Failed to start MDP mode\n"); goto exit; } } exit: return rc; } int mdp_stop(struct v4l2_subdev *sd, void *arg) { struct mdp_instance *inst = arg; int rc = 0; if (inst) { rc = msm_fb_writeback_stop(inst->mdp); if (rc) { WFD_MSG_ERR("Failed to stop writeback mode\n"); return rc; } } return 0; } static int mdp_close(struct v4l2_subdev *sd, void *arg) { struct mdp_instance *inst = arg; if (inst) { if (inst->secure) msm_fb_writeback_set_secure(inst->mdp, false); msm_fb_writeback_terminate(inst->mdp); kfree(inst); } return 0; } static int mdp_q_buffer(struct v4l2_subdev *sd, void *arg) { int rc = 0; struct mdp_buf_info *binfo = arg; struct msmfb_data fbdata; struct mdp_instance *inst; if (!binfo || !binfo->inst || !binfo->cookie) { WFD_MSG_ERR("Invalid argument\n"); return -EINVAL; } inst = binfo->inst; fbdata.offset = binfo->offset; fbdata.memory_id = binfo->fd; fbdata.iova = binfo->paddr; fbdata.id = 0; fbdata.flags = 0; fbdata.priv = (uint32_t)binfo->cookie; WFD_MSG_DBG("queue buffer to mdp with offset = %u, fd = %u, "\ "priv = %p, iova = %p\n", fbdata.offset, fbdata.memory_id, (void *)fbdata.priv, (void *)fbdata.iova); rc = msm_fb_writeback_queue_buffer(inst->mdp, &fbdata); if (rc) WFD_MSG_ERR("Failed to queue buffer\n"); return rc; } static int mdp_dq_buffer(struct v4l2_subdev *sd, void *arg) { int rc = 0; struct mdp_buf_info *obuf = arg; struct msmfb_data fbdata; struct mdp_instance *inst; if (!arg) { WFD_MSG_ERR("Invalid argument\n"); return -EINVAL; } inst = obuf->inst; fbdata.flags = MSMFB_WRITEBACK_DEQUEUE_BLOCKING; rc = msm_fb_writeback_dequeue_buffer(inst->mdp, &fbdata); if (rc) { WFD_MSG_ERR("Failed to dequeue buffer\n"); return rc; } WFD_MSG_DBG("dequeue buf from mdp with priv = %u\n", fbdata.priv); obuf->cookie = (void *)fbdata.priv; return rc; } static int mdp_set_prop(struct v4l2_subdev *sd, void *arg) { struct mdp_prop *prop = (struct mdp_prop *)arg; struct mdp_instance *inst = prop->inst; if (!prop || !inst) { WFD_MSG_ERR("Invalid arguments\n"); return -EINVAL; } inst->height = prop->height; inst->width = prop->width; return 0; } static int mdp_mmap(struct v4l2_subdev *sd, void *arg) { int rc = 0, align = 0; struct mem_region_map *mmap = arg; struct mem_region *mregion; int domain = -1; struct mdp_instance *inst = NULL; if (!mmap || !mmap->mregion || !mmap->cookie) { WFD_MSG_ERR("Invalid argument\n"); return -EINVAL; } inst = mmap->cookie; mregion = mmap->mregion; align = inst->secure ? SZ_1M : SZ_4K; if (mregion->size % align != 0) { WFD_MSG_ERR("Memregion not aligned to %d\n", align); return -EINVAL; } if (inst->secure) { rc = msm_ion_secure_buffer(mmap->ion_client, mregion->ion_handle, VIDEO_PIXEL, 0); if (rc) { WFD_MSG_ERR("Failed to secure input buffer\n"); goto secure_fail; } } domain = msm_fb_get_iommu_domain(inst->mdp, inst->secure ? MDP_IOMMU_DOMAIN_CP : MDP_IOMMU_DOMAIN_NS); rc = ion_map_iommu(mmap->ion_client, mregion->ion_handle, domain, 0, align, 0, (unsigned long *)&mregion->paddr, (unsigned long *)&mregion->size, 0, 0); if (rc) { WFD_MSG_ERR("Failed to map into %ssecure domain: %d\n", !inst->secure ? "non" : "", rc); goto iommu_fail; } return 0; iommu_fail: if (inst->secure) msm_ion_unsecure_buffer(mmap->ion_client, mregion->ion_handle); secure_fail: return rc; } static int mdp_munmap(struct v4l2_subdev *sd, void *arg) { struct mem_region_map *mmap = arg; struct mem_region *mregion; int domain = -1; struct mdp_instance *inst = NULL; if (!mmap || !mmap->mregion || !mmap->cookie) { WFD_MSG_ERR("Invalid argument\n"); return -EINVAL; } inst = mmap->cookie; mregion = mmap->mregion; domain = msm_fb_get_iommu_domain(inst->mdp, inst->secure ? MDP_IOMMU_DOMAIN_CP : MDP_IOMMU_DOMAIN_NS); ion_unmap_iommu(mmap->ion_client, mregion->ion_handle, domain, 0); if (inst->secure) msm_ion_unsecure_buffer(mmap->ion_client, mregion->ion_handle); return 0; } static int mdp_secure(struct v4l2_subdev *sd, void *arg) { struct mdp_instance *inst = NULL; int rc = 0; if (!arg) { WFD_MSG_ERR("Invalid argument\n"); return -EINVAL; } inst = arg; rc = msm_fb_writeback_set_secure(inst->mdp, true); if (!rc) inst->secure = true; return rc; } long mdp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { int rc = 0; if (!sd) { WFD_MSG_ERR("Invalid arguments\n"); return -EINVAL; } switch (cmd) { case MDP_Q_BUFFER: rc = mdp_q_buffer(sd, arg); break; case MDP_DQ_BUFFER: rc = mdp_dq_buffer(sd, arg); break; case MDP_OPEN: rc = mdp_open(sd, arg); break; case MDP_START: rc = mdp_start(sd, arg); break; case MDP_STOP: rc = mdp_stop(sd, arg); break; case MDP_SET_PROP: rc = mdp_set_prop(sd, arg); break; case MDP_CLOSE: rc = mdp_close(sd, arg); break; case MDP_MMAP: rc = mdp_mmap(sd, arg); break; case MDP_MUNMAP: rc = mdp_munmap(sd, arg); break; default: WFD_MSG_ERR("IOCTL: %u not supported\n", cmd); rc = -EINVAL; break; } return rc; }