int *plane_cnt)
{
int i, j;
+ struct dsim_device *dsim;
for (i = 0; i < decon->dt.max_win; i++) {
for (j = 0; j < plane_cnt[i]; ++j)
decon_free_dma_buf(decon, &dma_bufs[i][j]);
}
+ if (decon->dt.out_type == DECON_OUT_DSI) {
+ if (decon->lcd_info->mode == DECON_VIDEO_MODE) {
+ dsim = v4l2_get_subdevdata(decon->out_sd[0]);
+ if (dsim->fb_reservation) {
+ v4l2_subdev_call(decon->out_sd[0], core, ioctl,
+ DSIM_IOC_FREE_FB_RES, NULL);
+ }
+ }
+ }
+
if (decon->dt.out_type == DECON_OUT_WB) {
for (j = 0; j < plane_cnt[0]; ++j)
decon_free_dma_buf(decon,
int total_underrun_cnt;
struct backlight_device *bd;
int idle_ip_index;
+
+ /* true - fb reserved */
+ /* false - fb not reserved */
+ bool fb_reservation;
+ phys_addr_t phys_addr;
+ phys_addr_t phys_size;
};
struct dsim_lcd_driver {
#define DSIM_IOC_DUMP _IOW('D', 8, u32)
#define DSIM_IOC_GET_WCLK _IOW('D', 9, u32)
#define DSIM_IOC_SET_CONFIG _IOW('D', 10, u32)
+#define DSIM_IOC_FREE_FB_RES _IOW('D', 11, u32)
#define DSIM_IOC_DOZE _IOW('D', 20, u32)
#define DSIM_IOC_DOZE_SUSPEND _IOW('D', 21, u32)
#include <linux/exynos_iovmm.h>
#endif
+#include <linux/of_reserved_mem.h>
+#include "../../../../../mm/internal.h"
+
#include "decon.h"
#include "dsim.h"
return dsim_disable(dsim);
}
+static int dsim_free_fb_resource(struct dsim_device *dsim)
+{
+ dsim_info("one to one unmapping: 0x%x\n", dsim->phys_addr);
+
+ /* unmap */
+ iovmm_unmap_oto(dsim->dev, dsim->phys_addr);
+
+ /* unreserve memory */
+ of_reserved_mem_device_release(dsim->dev);
+
+ /* update state */
+ dsim->fb_reservation = false;
+ dsim->phys_addr = 0xdead;
+ dsim->phys_size = 0;
+
+ return 0;
+}
+
+static int dsim_acquire_fb_resource(struct dsim_device *dsim)
+{
+ int ret = 0;
+
+ ret = of_reserved_mem_device_init_by_idx(dsim->dev, dsim->dev->of_node, 0);
+ if (ret) {
+ dsim_err("failed reserved mem device init: %d\n", ret);
+ dsim->fb_reservation = false;
+ goto err;
+ } else
+ dsim->fb_reservation = true;
+
+ ret = iovmm_map_oto(dsim->dev, dsim->phys_addr, dsim->phys_size);
+ if (ret)
+ dsim_err("failed one to one mapping: %d\n", ret);
+ else
+ dsim_info("one to one mapping: 0x%x, 0x%x\n",
+ dsim->phys_addr,
+ dsim->phys_size);
+err:
+ return ret;
+}
+
static long dsim_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct dsim_device *dsim = container_of(sd, struct dsim_device, sd);
case EXYNOS_DPU_GET_ACLK:
return clk_get_rate(dsim->res.aclk);
+ case DSIM_IOC_FREE_FB_RES:
+ ret = dsim_free_fb_resource(dsim);
+ break;
+
case DSIM_IOC_DOZE:
ret = dsim_doze(dsim);
break;
exynos_update_ip_idle_status(dsim->idle_ip_index, 1);
#endif
+ if (dsim->lcd_info.mode == DECON_VIDEO_MODE)
+ dsim_acquire_fb_resource(dsim);
+
pm_runtime_enable(dev);
ret = iovmm_activate(dev);
}
module_exit(dsim_exit);
+
+static int rmem_device_init(struct reserved_mem *rmem, struct device *dev)
+{
+ struct dsim_device *dsim = dev_get_drvdata(dev);
+
+ dsim->phys_addr = rmem->base;
+ dsim->phys_size = rmem->size;
+
+ return 0;
+}
+
+/* of_reserved_mem_device_release(dev) when reserved memory is no logner required */
+static void rmem_device_release(struct reserved_mem *rmem, struct device *dev)
+{
+ struct page *first = phys_to_page(PAGE_ALIGN(rmem->base));
+ struct page *last = phys_to_page((rmem->base + rmem->size) & PAGE_MASK);
+ struct page *page;
+
+ pr_info("%s: base=%pa, size=%pa, first=%pa, last=%pa\n",
+ __func__, &rmem->base, &rmem->size, first, last);
+
+ for (page = first; page != last; page++) {
+ __ClearPageReserved(page);
+ set_page_count(page, 1);
+ __free_pages(page, 0);
+ adjust_managed_page_count(page, 1);
+ }
+}
+
+static const struct reserved_mem_ops rmem_ops = {
+ .device_init = rmem_device_init,
+ .device_release = rmem_device_release,
+};
+
+static int __init fb_rmem_setup(struct reserved_mem *rmem)
+{
+ pr_info("%s: base=%pa, size=%pa\n", __func__, &rmem->base, &rmem->size);
+
+ rmem->ops = &rmem_ops;
+ return 0;
+}
+RESERVEDMEM_OF_DECLARE(fb_rmem, "exynos,fb_rmem", fb_rmem_setup);
+
MODULE_AUTHOR("Yeongran Shin <yr613.shin@samsung.com>");
MODULE_DESCRIPTION("Samusung EXYNOS DSIM driver");
MODULE_LICENSE("GPL");