| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2015-2016 MediaTek Inc. |
|---|
| 3 | 4 | * Author: Houlong Wei <houlong.wei@mediatek.com> |
|---|
| 4 | 5 | * Ming Hsiu Tsai <minghsiu.tsai@mediatek.com> |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 8 | | - * published by the Free Software Foundation. |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | | - * GNU General Public License for more details. |
|---|
| 14 | 6 | */ |
|---|
| 15 | 7 | |
|---|
| 16 | 8 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 63 | 55 | static void mtk_mdp_clock_on(struct mtk_mdp_dev *mdp) |
|---|
| 64 | 56 | { |
|---|
| 65 | 57 | struct device *dev = &mdp->pdev->dev; |
|---|
| 66 | | - int i; |
|---|
| 58 | + struct mtk_mdp_comp *comp_node; |
|---|
| 67 | 59 | |
|---|
| 68 | | - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) |
|---|
| 69 | | - mtk_mdp_comp_clock_on(dev, mdp->comp[i]); |
|---|
| 60 | + list_for_each_entry(comp_node, &mdp->comp_list, node) |
|---|
| 61 | + mtk_mdp_comp_clock_on(dev, comp_node); |
|---|
| 70 | 62 | } |
|---|
| 71 | 63 | |
|---|
| 72 | 64 | static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp) |
|---|
| 73 | 65 | { |
|---|
| 74 | 66 | struct device *dev = &mdp->pdev->dev; |
|---|
| 75 | | - int i; |
|---|
| 67 | + struct mtk_mdp_comp *comp_node; |
|---|
| 76 | 68 | |
|---|
| 77 | | - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) |
|---|
| 78 | | - mtk_mdp_comp_clock_off(dev, mdp->comp[i]); |
|---|
| 69 | + list_for_each_entry(comp_node, &mdp->comp_list, node) |
|---|
| 70 | + mtk_mdp_comp_clock_off(dev, comp_node); |
|---|
| 79 | 71 | } |
|---|
| 80 | 72 | |
|---|
| 81 | 73 | static void mtk_mdp_wdt_worker(struct work_struct *work) |
|---|
| .. | .. |
|---|
| 99 | 91 | queue_work(mdp->wdt_wq, &mdp->wdt_work); |
|---|
| 100 | 92 | } |
|---|
| 101 | 93 | |
|---|
| 94 | +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, |
|---|
| 95 | + struct mtk_mdp_comp *comp) |
|---|
| 96 | +{ |
|---|
| 97 | + list_add(&comp->node, &mdp->comp_list); |
|---|
| 98 | +} |
|---|
| 99 | + |
|---|
| 100 | +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, |
|---|
| 101 | + struct mtk_mdp_comp *comp) |
|---|
| 102 | +{ |
|---|
| 103 | + list_del(&comp->node); |
|---|
| 104 | +} |
|---|
| 105 | + |
|---|
| 102 | 106 | static int mtk_mdp_probe(struct platform_device *pdev) |
|---|
| 103 | 107 | { |
|---|
| 104 | 108 | struct mtk_mdp_dev *mdp; |
|---|
| 105 | 109 | struct device *dev = &pdev->dev; |
|---|
| 106 | 110 | struct device_node *node, *parent; |
|---|
| 107 | | - int i, ret = 0; |
|---|
| 111 | + struct mtk_mdp_comp *comp, *comp_temp; |
|---|
| 112 | + int ret = 0; |
|---|
| 108 | 113 | |
|---|
| 109 | 114 | mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL); |
|---|
| 110 | 115 | if (!mdp) |
|---|
| .. | .. |
|---|
| 112 | 117 | |
|---|
| 113 | 118 | mdp->id = pdev->id; |
|---|
| 114 | 119 | mdp->pdev = pdev; |
|---|
| 120 | + INIT_LIST_HEAD(&mdp->comp_list); |
|---|
| 115 | 121 | INIT_LIST_HEAD(&mdp->ctx_list); |
|---|
| 116 | 122 | |
|---|
| 117 | 123 | mutex_init(&mdp->lock); |
|---|
| .. | .. |
|---|
| 131 | 137 | for_each_child_of_node(parent, node) { |
|---|
| 132 | 138 | const struct of_device_id *of_id; |
|---|
| 133 | 139 | enum mtk_mdp_comp_type comp_type; |
|---|
| 134 | | - int comp_id; |
|---|
| 135 | | - struct mtk_mdp_comp *comp; |
|---|
| 136 | 140 | |
|---|
| 137 | 141 | of_id = of_match_node(mtk_mdp_comp_dt_ids, node); |
|---|
| 138 | 142 | if (!of_id) |
|---|
| .. | .. |
|---|
| 145 | 149 | } |
|---|
| 146 | 150 | |
|---|
| 147 | 151 | comp_type = (enum mtk_mdp_comp_type)of_id->data; |
|---|
| 148 | | - comp_id = mtk_mdp_comp_get_id(dev, node, comp_type); |
|---|
| 149 | | - if (comp_id < 0) { |
|---|
| 150 | | - dev_warn(dev, "Skipping unknown component %pOF\n", |
|---|
| 151 | | - node); |
|---|
| 152 | | - continue; |
|---|
| 153 | | - } |
|---|
| 154 | 152 | |
|---|
| 155 | 153 | comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); |
|---|
| 156 | 154 | if (!comp) { |
|---|
| 157 | 155 | ret = -ENOMEM; |
|---|
| 156 | + of_node_put(node); |
|---|
| 158 | 157 | goto err_comp; |
|---|
| 159 | 158 | } |
|---|
| 160 | | - mdp->comp[comp_id] = comp; |
|---|
| 161 | 159 | |
|---|
| 162 | | - ret = mtk_mdp_comp_init(dev, node, comp, comp_id); |
|---|
| 163 | | - if (ret) |
|---|
| 160 | + ret = mtk_mdp_comp_init(dev, node, comp, comp_type); |
|---|
| 161 | + if (ret) { |
|---|
| 162 | + of_node_put(node); |
|---|
| 164 | 163 | goto err_comp; |
|---|
| 164 | + } |
|---|
| 165 | + |
|---|
| 166 | + mtk_mdp_register_component(mdp, comp); |
|---|
| 165 | 167 | } |
|---|
| 166 | 168 | |
|---|
| 167 | 169 | mdp->job_wq = create_singlethread_workqueue(MTK_MDP_MODULE_NAME); |
|---|
| .. | .. |
|---|
| 193 | 195 | } |
|---|
| 194 | 196 | |
|---|
| 195 | 197 | mdp->vpu_dev = vpu_get_plat_device(pdev); |
|---|
| 196 | | - vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, |
|---|
| 197 | | - VPU_RST_MDP); |
|---|
| 198 | + ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, |
|---|
| 199 | + VPU_RST_MDP); |
|---|
| 200 | + if (ret) { |
|---|
| 201 | + dev_err(&pdev->dev, "Failed to register reset handler\n"); |
|---|
| 202 | + goto err_m2m_register; |
|---|
| 203 | + } |
|---|
| 198 | 204 | |
|---|
| 199 | 205 | platform_set_drvdata(pdev, mdp); |
|---|
| 200 | 206 | |
|---|
| 201 | | - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); |
|---|
| 207 | + ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); |
|---|
| 208 | + if (ret) { |
|---|
| 209 | + dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n"); |
|---|
| 210 | + goto err_m2m_register; |
|---|
| 211 | + } |
|---|
| 202 | 212 | |
|---|
| 203 | 213 | pm_runtime_enable(dev); |
|---|
| 204 | 214 | dev_dbg(dev, "mdp-%d registered successfully\n", mdp->id); |
|---|
| .. | .. |
|---|
| 217 | 227 | err_alloc_job_wq: |
|---|
| 218 | 228 | |
|---|
| 219 | 229 | err_comp: |
|---|
| 220 | | - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) |
|---|
| 221 | | - mtk_mdp_comp_deinit(dev, mdp->comp[i]); |
|---|
| 230 | + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { |
|---|
| 231 | + mtk_mdp_unregister_component(mdp, comp); |
|---|
| 232 | + mtk_mdp_comp_deinit(dev, comp); |
|---|
| 233 | + } |
|---|
| 222 | 234 | |
|---|
| 223 | 235 | dev_dbg(dev, "err %d\n", ret); |
|---|
| 224 | 236 | return ret; |
|---|
| .. | .. |
|---|
| 227 | 239 | static int mtk_mdp_remove(struct platform_device *pdev) |
|---|
| 228 | 240 | { |
|---|
| 229 | 241 | struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev); |
|---|
| 230 | | - int i; |
|---|
| 242 | + struct mtk_mdp_comp *comp, *comp_temp; |
|---|
| 231 | 243 | |
|---|
| 232 | 244 | pm_runtime_disable(&pdev->dev); |
|---|
| 233 | 245 | vb2_dma_contig_clear_max_seg_size(&pdev->dev); |
|---|
| 234 | 246 | mtk_mdp_unregister_m2m_device(mdp); |
|---|
| 235 | 247 | v4l2_device_unregister(&mdp->v4l2_dev); |
|---|
| 236 | 248 | |
|---|
| 249 | + flush_workqueue(mdp->wdt_wq); |
|---|
| 250 | + destroy_workqueue(mdp->wdt_wq); |
|---|
| 251 | + |
|---|
| 237 | 252 | flush_workqueue(mdp->job_wq); |
|---|
| 238 | 253 | destroy_workqueue(mdp->job_wq); |
|---|
| 239 | 254 | |
|---|
| 240 | | - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) |
|---|
| 241 | | - mtk_mdp_comp_deinit(&pdev->dev, mdp->comp[i]); |
|---|
| 255 | + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { |
|---|
| 256 | + mtk_mdp_unregister_component(mdp, comp); |
|---|
| 257 | + mtk_mdp_comp_deinit(&pdev->dev, comp); |
|---|
| 258 | + } |
|---|
| 242 | 259 | |
|---|
| 243 | 260 | dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); |
|---|
| 244 | 261 | return 0; |
|---|