1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * rcar-fcp.c -- R-Car Frame Compression Processor Driver 4 * 5 * Copyright (C) 2016 Renesas Electronics Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10#include <linux/device.h> 11#include <linux/dma-mapping.h> 12#include <linux/list.h> 13#include <linux/module.h> 14#include <linux/mod_devicetable.h> 15#include <linux/mutex.h> 16#include <linux/platform_device.h> 17#include <linux/pm_runtime.h> 18#include <linux/slab.h> 19 20#include <media/rcar-fcp.h> 21 22struct rcar_fcp_device { 23 struct list_head list; 24 struct device *dev; 25}; 26 27static LIST_HEAD(fcp_devices); 28static DEFINE_MUTEX(fcp_lock); 29 30/* ----------------------------------------------------------------------------- 31 * Public API 32 */ 33 34/** 35 * rcar_fcp_get - Find and acquire a reference to an FCP instance 36 * @np: Device node of the FCP instance 37 * 38 * Search the list of registered FCP instances for the instance corresponding to 39 * the given device node. 40 * 41 * Return a pointer to the FCP instance, or an ERR_PTR if the instance can't be 42 * found. 43 */ 44struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np) 45{ 46 struct rcar_fcp_device *fcp; 47 48 mutex_lock(&fcp_lock); 49 50 list_for_each_entry(fcp, &fcp_devices, list) { 51 if (fcp->dev->of_node != np) 52 continue; 53 54 get_device(fcp->dev); 55 goto done; 56 } 57 58 fcp = ERR_PTR(-EPROBE_DEFER); 59 60done: 61 mutex_unlock(&fcp_lock); 62 return fcp; 63} 64EXPORT_SYMBOL_GPL(rcar_fcp_get); 65 66/** 67 * rcar_fcp_put - Release a reference to an FCP instance 68 * @fcp: The FCP instance 69 * 70 * Release the FCP instance acquired by a call to rcar_fcp_get(). 71 */ 72void rcar_fcp_put(struct rcar_fcp_device *fcp) 73{ 74 if (fcp) 75 put_device(fcp->dev); 76} 77EXPORT_SYMBOL_GPL(rcar_fcp_put); 78 79struct device *rcar_fcp_get_device(struct rcar_fcp_device *fcp) 80{ 81 return fcp->dev; 82} 83EXPORT_SYMBOL_GPL(rcar_fcp_get_device); 84 85/** 86 * rcar_fcp_enable - Enable an FCP 87 * @fcp: The FCP instance 88 * 89 * Before any memory access through an FCP is performed by a module, the FCP 90 * must be enabled by a call to this function. The enable calls are reference 91 * counted, each successful call must be followed by one rcar_fcp_disable() 92 * call when no more memory transfer can occur through the FCP. 93 * 94 * Return 0 on success or a negative error code if an error occurs. The enable 95 * reference count isn't increased when this function returns an error. 96 */ 97int rcar_fcp_enable(struct rcar_fcp_device *fcp) 98{ 99 int ret; 100 101 if (!fcp) 102 return 0; 103 104 ret = pm_runtime_get_sync(fcp->dev); 105 if (ret < 0) { 106 pm_runtime_put_noidle(fcp->dev); 107 return ret; 108 } 109 110 return 0; 111} 112EXPORT_SYMBOL_GPL(rcar_fcp_enable); 113 114/** 115 * rcar_fcp_disable - Disable an FCP 116 * @fcp: The FCP instance 117 * 118 * This function is the counterpart of rcar_fcp_enable(). As enable calls are 119 * reference counted a disable call may not disable the FCP synchronously. 120 */ 121void rcar_fcp_disable(struct rcar_fcp_device *fcp) 122{ 123 if (fcp) 124 pm_runtime_put(fcp->dev); 125} 126EXPORT_SYMBOL_GPL(rcar_fcp_disable); 127 128/* ----------------------------------------------------------------------------- 129 * Platform Driver 130 */ 131 132static int rcar_fcp_probe(struct platform_device *pdev) 133{ 134 struct rcar_fcp_device *fcp; 135 136 fcp = devm_kzalloc(&pdev->dev, sizeof(*fcp), GFP_KERNEL); 137 if (fcp == NULL) 138 return -ENOMEM; 139 140 fcp->dev = &pdev->dev; 141 142 dma_set_max_seg_size(fcp->dev, UINT_MAX); 143 144 pm_runtime_enable(&pdev->dev); 145 146 mutex_lock(&fcp_lock); 147 list_add_tail(&fcp->list, &fcp_devices); 148 mutex_unlock(&fcp_lock); 149 150 platform_set_drvdata(pdev, fcp); 151 152 return 0; 153} 154 155static int rcar_fcp_remove(struct platform_device *pdev) 156{ 157 struct rcar_fcp_device *fcp = platform_get_drvdata(pdev); 158 159 mutex_lock(&fcp_lock); 160 list_del(&fcp->list); 161 mutex_unlock(&fcp_lock); 162 163 pm_runtime_disable(&pdev->dev); 164 165 return 0; 166} 167 168static const struct of_device_id rcar_fcp_of_match[] = { 169 { .compatible = "renesas,fcpf" }, 170 { .compatible = "renesas,fcpv" }, 171 { }, 172}; 173MODULE_DEVICE_TABLE(of, rcar_fcp_of_match); 174 175static struct platform_driver rcar_fcp_platform_driver = { 176 .probe = rcar_fcp_probe, 177 .remove = rcar_fcp_remove, 178 .driver = { 179 .name = "rcar-fcp", 180 .of_match_table = rcar_fcp_of_match, 181 .suppress_bind_attrs = true, 182 }, 183}; 184 185module_platform_driver(rcar_fcp_platform_driver); 186 187MODULE_ALIAS("rcar-fcp"); 188MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 189MODULE_DESCRIPTION("Renesas FCP Driver"); 190MODULE_LICENSE("GPL"); 191