FILE
dcmipp-core.c
File preview with syntax highlighting.
linux-6.19.14/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Driver for STM32 Digital Camera Memory Interface Pixel Processor 4 * 5 * Copyright (C) STMicroelectronics SA 2023 6 * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> 7 * Alain Volmat <alain.volmat@foss.st.com> 8 * for STMicroelectronics. 9 */ 10 11#include <linux/clk.h> 12#include <linux/delay.h> 13#include <linux/init.h> 14#include <linux/module.h> 15#include <linux/pinctrl/consumer.h> 16#include <linux/platform_device.h> 17#include <linux/pm_runtime.h> 18#include <linux/property.h> 19#include <linux/reset.h> 20#include <media/media-device.h> 21#include <media/v4l2-device.h> 22#include <media/v4l2-fwnode.h> 23 24#include "dcmipp-common.h" 25 26#define DCMIPP_MDEV_MODEL_NAME "DCMIPP MDEV" 27 28#define DCMIPP_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \ 29 .src_ent = src, \ 30 .src_pad = srcpad, \ 31 .sink_ent = sink, \ 32 .sink_pad = sinkpad, \ 33 .flags = link_flags, \ 34} 35 36struct dcmipp_device { 37 /* The platform device */ 38 struct platform_device pdev; 39 struct device *dev; 40 41 /* Hardware resources */ 42 void __iomem *regs; 43 struct clk *mclk; 44 struct clk *kclk; 45 46 /* The pipeline configuration */ 47 const struct dcmipp_pipeline_config *pipe_cfg; 48 49 /* The Associated media_device parent */ 50 struct media_device mdev; 51 52 /* Internal v4l2 parent device*/ 53 struct v4l2_device v4l2_dev; 54 55 /* Entities */ 56 struct dcmipp_ent_device **entity; 57 58 struct v4l2_async_notifier notifier; 59}; 60 61static inline struct dcmipp_device * 62notifier_to_dcmipp(struct v4l2_async_notifier *n) 63{ 64 return container_of(n, struct dcmipp_device, notifier); 65} 66 67/* Structure which describes individual configuration for each entity */ 68struct dcmipp_ent_config { 69 const char *name; 70 struct dcmipp_ent_device *(*init) 71 (struct device *dev, const char *entity_name, 72 struct v4l2_device *v4l2_dev, void __iomem *regs); 73 void (*release)(struct dcmipp_ent_device *ved); 74}; 75 76/* Structure which describes links between entities */ 77struct dcmipp_ent_link { 78 unsigned int src_ent; 79 u16 src_pad; 80 unsigned int sink_ent; 81 u16 sink_pad; 82 u32 flags; 83}; 84 85/* Structure which describes the whole topology */ 86struct dcmipp_pipeline_config { 87 const struct dcmipp_ent_config *ents; 88 size_t num_ents; 89 const struct dcmipp_ent_link *links; 90 size_t num_links; 91 u32 hw_revision; 92 bool has_csi2; 93 bool needs_mclk; 94}; 95 96/* -------------------------------------------------------------------------- 97 * Topology Configuration 98 */ 99 100static const struct dcmipp_ent_config stm32mp13_ent_config[] = { 101 { 102 .name = "dcmipp_input", 103 .init = dcmipp_inp_ent_init, 104 .release = dcmipp_inp_ent_release, 105 }, 106 { 107 .name = "dcmipp_dump_postproc", 108 .init = dcmipp_byteproc_ent_init, 109 .release = dcmipp_byteproc_ent_release, 110 }, 111 { 112 .name = "dcmipp_dump_capture", 113 .init = dcmipp_bytecap_ent_init, 114 .release = dcmipp_bytecap_ent_release, 115 }, 116}; 117 118#define ID_INPUT 0 119#define ID_DUMP_BYTEPROC 1 120#define ID_DUMP_CAPTURE 2 121 122static const struct dcmipp_ent_link stm32mp13_ent_links[] = { 123 DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, 124 MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), 125 DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, 126 MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), 127}; 128 129#define DCMIPP_STM32MP13_VERR 0x10 130static const struct dcmipp_pipeline_config stm32mp13_pipe_cfg = { 131 .ents = stm32mp13_ent_config, 132 .num_ents = ARRAY_SIZE(stm32mp13_ent_config), 133 .links = stm32mp13_ent_links, 134 .num_links = ARRAY_SIZE(stm32mp13_ent_links), 135 .hw_revision = DCMIPP_STM32MP13_VERR 136}; 137 138static const struct dcmipp_ent_config stm32mp25_ent_config[] = { 139 { 140 .name = "dcmipp_input", 141 .init = dcmipp_inp_ent_init, 142 .release = dcmipp_inp_ent_release, 143 }, 144 { 145 .name = "dcmipp_dump_postproc", 146 .init = dcmipp_byteproc_ent_init, 147 .release = dcmipp_byteproc_ent_release, 148 }, 149 { 150 .name = "dcmipp_dump_capture", 151 .init = dcmipp_bytecap_ent_init, 152 .release = dcmipp_bytecap_ent_release, 153 }, 154}; 155 156static const struct dcmipp_ent_link stm32mp25_ent_links[] = { 157 DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, 158 MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), 159 DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, 160 MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), 161}; 162 163#define DCMIPP_STM32MP25_VERR 0x30 164static const struct dcmipp_pipeline_config stm32mp25_pipe_cfg = { 165 .ents = stm32mp25_ent_config, 166 .num_ents = ARRAY_SIZE(stm32mp25_ent_config), 167 .links = stm32mp25_ent_links, 168 .num_links = ARRAY_SIZE(stm32mp25_ent_links), 169 .hw_revision = DCMIPP_STM32MP25_VERR, 170 .has_csi2 = true, 171 .needs_mclk = true 172}; 173 174#define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\ 175 (f) == MEDIA_LNK_FL_ENABLED ? "ENABLED" :\ 176 (f) == MEDIA_LNK_FL_IMMUTABLE ? "IMMUTABLE" :\ 177 (f) == (MEDIA_LNK_FL_ENABLED |\ 178 MEDIA_LNK_FL_IMMUTABLE) ?\ 179 "ENABLED, IMMUTABLE" :\ 180 "UNKNOWN") 181 182static int dcmipp_create_links(struct dcmipp_device *dcmipp) 183{ 184 unsigned int i; 185 int ret; 186 187 /* Initialize the links between entities */ 188 for (i = 0; i < dcmipp->pipe_cfg->num_links; i++) { 189 const struct dcmipp_ent_link *link = 190 &dcmipp->pipe_cfg->links[i]; 191 struct dcmipp_ent_device *ved_src = 192 dcmipp->entity[link->src_ent]; 193 struct dcmipp_ent_device *ved_sink = 194 dcmipp->entity[link->sink_ent]; 195 196 dev_dbg(dcmipp->dev, "Create link \"%s\":%d -> %d:\"%s\" [%s]\n", 197 dcmipp->pipe_cfg->ents[link->src_ent].name, 198 link->src_pad, link->sink_pad, 199 dcmipp->pipe_cfg->ents[link->sink_ent].name, 200 LINK_FLAG_TO_STR(link->flags)); 201 202 ret = media_create_pad_link(ved_src->ent, link->src_pad, 203 ved_sink->ent, link->sink_pad, 204 link->flags); 205 if (ret) 206 return ret; 207 } 208 209 return 0; 210} 211 212static int dcmipp_graph_init(struct dcmipp_device *dcmipp); 213 214static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp) 215{ 216 int ret, i; 217 218 /* Call all subdev inits */ 219 for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { 220 const char *name = dcmipp->pipe_cfg->ents[i].name; 221 222 dev_dbg(dcmipp->dev, "add subdev %s\n", name); 223 dcmipp->entity[i] = 224 dcmipp->pipe_cfg->ents[i].init(dcmipp->dev, name, 225 &dcmipp->v4l2_dev, 226 dcmipp->regs); 227 if (IS_ERR(dcmipp->entity[i])) { 228 dev_err(dcmipp->dev, "failed to init subdev %s\n", 229 name); 230 ret = PTR_ERR(dcmipp->entity[i]); 231 goto err_init_entity; 232 } 233 } 234 235 /* Initialize links */ 236 ret = dcmipp_create_links(dcmipp); 237 if (ret) 238 goto err_init_entity; 239 240 ret = dcmipp_graph_init(dcmipp); 241 if (ret < 0) 242 goto err_init_entity; 243 244 return 0; 245 246err_init_entity: 247 while (i-- > 0) 248 dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]); 249 return ret; 250} 251 252static const struct of_device_id dcmipp_of_match[] = { 253 { .compatible = "st,stm32mp13-dcmipp", .data = &stm32mp13_pipe_cfg }, 254 { .compatible = "st,stm32mp25-dcmipp", .data = &stm32mp25_pipe_cfg }, 255 { /* end node */ }, 256}; 257MODULE_DEVICE_TABLE(of, dcmipp_of_match); 258 259static irqreturn_t dcmipp_irq_thread(int irq, void *arg) 260{ 261 struct dcmipp_device *dcmipp = arg; 262 struct dcmipp_ent_device *ved; 263 unsigned int i; 264 265 /* Call irq thread of each entities of pipeline */ 266 for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { 267 ved = dcmipp->entity[i]; 268 if (ved->thread_fn && ved->handler_ret == IRQ_WAKE_THREAD) 269 ved->thread_fn(irq, ved); 270 } 271 272 return IRQ_HANDLED; 273} 274 275static irqreturn_t dcmipp_irq_callback(int irq, void *arg) 276{ 277 struct dcmipp_device *dcmipp = arg; 278 struct dcmipp_ent_device *ved; 279 irqreturn_t ret = IRQ_HANDLED; 280 unsigned int i; 281 282 /* Call irq handler of each entities of pipeline */ 283 for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) { 284 ved = dcmipp->entity[i]; 285 if (ved->handler) 286 ved->handler_ret = ved->handler(irq, ved); 287 else if (ved->thread_fn) 288 ved->handler_ret = IRQ_WAKE_THREAD; 289 else 290 ved->handler_ret = IRQ_HANDLED; 291 if (ved->handler_ret != IRQ_HANDLED) 292 ret = ved->handler_ret; 293 } 294 295 return ret; 296} 297 298static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, 299 struct v4l2_subdev *subdev, 300 struct v4l2_async_connection *asd) 301{ 302 struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); 303 int ret = -EINVAL; 304 int src_pad, i; 305 struct dcmipp_ent_device *sink; 306 struct v4l2_fwnode_endpoint vep = { 0 }; 307 struct fwnode_handle *ep; 308 enum v4l2_mbus_type supported_types[] = { 309 V4L2_MBUS_PARALLEL, V4L2_MBUS_BT656, V4L2_MBUS_CSI2_DPHY 310 }; 311 312 dev_dbg(dcmipp->dev, "Subdev \"%s\" bound\n", subdev->name); 313 314 /* 315 * Link this sub-device to DCMIPP, it could be 316 * a parallel camera sensor or a CSI-2 to parallel bridge 317 */ 318 src_pad = media_entity_get_fwnode_pad(&subdev->entity, 319 subdev->fwnode, 320 MEDIA_PAD_FL_SOURCE); 321 322 /* Get bus characteristics from devicetree */ 323 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dcmipp->dev), 0, 0, 324 FWNODE_GRAPH_ENDPOINT_NEXT); 325 if (!ep) { 326 dev_err(dcmipp->dev, "Could not find the endpoint\n"); 327 return -ENODEV; 328 } 329 330 /* Check for supported MBUS type */ 331 for (i = 0; i < ARRAY_SIZE(supported_types); i++) { 332 /* Only MP25 supports CSI input */ 333 if (supported_types[i] == V4L2_MBUS_CSI2_DPHY && 334 !dcmipp->pipe_cfg->has_csi2) 335 continue; 336 337 vep.bus_type = supported_types[i]; 338 ret = v4l2_fwnode_endpoint_parse(ep, &vep); 339 if (!ret) 340 break; 341 } 342 343 fwnode_handle_put(ep); 344 345 if (ret) { 346 dev_err(dcmipp->dev, "Could not parse the endpoint\n"); 347 return ret; 348 } 349 350 if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && 351 vep.bus.parallel.bus_width == 0) { 352 dev_err(dcmipp->dev, "Invalid parallel interface bus-width\n"); 353 return -ENODEV; 354 } 355 356 /* Only 8 bits bus width supported with BT656 bus */ 357 if (vep.bus_type == V4L2_MBUS_BT656 && 358 vep.bus.parallel.bus_width != 8) { 359 dev_err(dcmipp->dev, "BT656 bus conflicts with %u bits bus width (8 bits required)\n", 360 vep.bus.parallel.bus_width); 361 return -ENODEV; 362 } 363 364 /* Connect input device to the dcmipp_input subdev */ 365 sink = dcmipp->entity[ID_INPUT]; 366 if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) { 367 sink->bus.flags = vep.bus.parallel.flags; 368 sink->bus.bus_width = vep.bus.parallel.bus_width; 369 sink->bus.data_shift = vep.bus.parallel.data_shift; 370 } 371 sink->bus_type = vep.bus_type; 372 ret = media_create_pad_link(&subdev->entity, src_pad, sink->ent, 0, 373 MEDIA_LNK_FL_IMMUTABLE | 374 MEDIA_LNK_FL_ENABLED); 375 if (ret) { 376 dev_err(dcmipp->dev, "Failed to create media pad link with subdev \"%s\"\n", 377 subdev->name); 378 return ret; 379 } 380 381 dev_dbg(dcmipp->dev, "DCMIPP is now linked to \"%s\"\n", subdev->name); 382 383 return 0; 384} 385 386static void dcmipp_graph_notify_unbind(struct v4l2_async_notifier *notifier, 387 struct v4l2_subdev *sd, 388 struct v4l2_async_connection *asd) 389{ 390 struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); 391 392 dev_dbg(dcmipp->dev, "Removing %s\n", sd->name); 393} 394 395static int dcmipp_graph_notify_complete(struct v4l2_async_notifier *notifier) 396{ 397 struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); 398 int ret; 399 400 /* Register the media device */ 401 ret = media_device_register(&dcmipp->mdev); 402 if (ret) { 403 dev_err(dcmipp->mdev.dev, 404 "media device register failed (err=%d)\n", ret); 405 return ret; 406 } 407 408 /* Expose all subdev's nodes*/ 409 ret = v4l2_device_register_subdev_nodes(&dcmipp->v4l2_dev); 410 if (ret) { 411 dev_err(dcmipp->mdev.dev, 412 "dcmipp subdev nodes registration failed (err=%d)\n", 413 ret); 414 media_device_unregister(&dcmipp->mdev); 415 return ret; 416 } 417 418 dev_dbg(dcmipp->dev, "Notify complete !\n"); 419 420 return 0; 421} 422 423static const struct v4l2_async_notifier_operations dcmipp_graph_notify_ops = { 424 .bound = dcmipp_graph_notify_bound, 425 .unbind = dcmipp_graph_notify_unbind, 426 .complete = dcmipp_graph_notify_complete, 427}; 428 429static int dcmipp_graph_init(struct dcmipp_device *dcmipp) 430{ 431 struct v4l2_async_connection *asd; 432 struct fwnode_handle *ep; 433 int ret; 434 435 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dcmipp->dev), 0, 0, 436 FWNODE_GRAPH_ENDPOINT_NEXT); 437 if (!ep) { 438 dev_err(dcmipp->dev, "Failed to get next endpoint\n"); 439 return -EINVAL; 440 } 441 442 v4l2_async_nf_init(&dcmipp->notifier, &dcmipp->v4l2_dev); 443 444 asd = v4l2_async_nf_add_fwnode_remote(&dcmipp->notifier, ep, 445 struct v4l2_async_connection); 446 447 fwnode_handle_put(ep); 448 449 if (IS_ERR(asd)) { 450 dev_err(dcmipp->dev, "Failed to add fwnode remote subdev\n"); 451 return PTR_ERR(asd); 452 } 453 454 dcmipp->notifier.ops = &dcmipp_graph_notify_ops; 455 456 ret = v4l2_async_nf_register(&dcmipp->notifier); 457 if (ret < 0) { 458 dev_err(dcmipp->dev, "Failed to register notifier\n"); 459 v4l2_async_nf_cleanup(&dcmipp->notifier); 460 return ret; 461 } 462 463 return 0; 464} 465 466static int dcmipp_probe(struct platform_device *pdev) 467{ 468 struct dcmipp_device *dcmipp; 469 struct clk *kclk, *mclk; 470 const struct dcmipp_pipeline_config *pipe_cfg; 471 struct reset_control *rstc; 472 int irq; 473 int ret; 474 475 dcmipp = devm_kzalloc(&pdev->dev, sizeof(*dcmipp), GFP_KERNEL); 476 if (!dcmipp) 477 return -ENOMEM; 478 479 dcmipp->dev = &pdev->dev; 480 481 pipe_cfg = device_get_match_data(dcmipp->dev); 482 if (!pipe_cfg) { 483 dev_err(&pdev->dev, "Can't get device data\n"); 484 return -ENODEV; 485 } 486 dcmipp->pipe_cfg = pipe_cfg; 487 488 platform_set_drvdata(pdev, dcmipp); 489 490 /* Get hardware resources from devicetree */ 491 rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 492 if (IS_ERR(rstc)) 493 return dev_err_probe(&pdev->dev, PTR_ERR(rstc), 494 "Could not get reset control\n"); 495 496 irq = platform_get_irq(pdev, 0); 497 if (irq < 0) 498 return irq; 499 500 dcmipp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 501 if (IS_ERR(dcmipp->regs)) { 502 dev_err(&pdev->dev, "Could not map registers\n"); 503 return PTR_ERR(dcmipp->regs); 504 } 505 506 ret = devm_request_threaded_irq(&pdev->dev, irq, dcmipp_irq_callback, 507 dcmipp_irq_thread, IRQF_ONESHOT, 508 dev_name(&pdev->dev), dcmipp); 509 if (ret) { 510 dev_err(&pdev->dev, "Unable to request irq %d\n", irq); 511 return ret; 512 } 513 514 /* Reset device */ 515 ret = reset_control_assert(rstc); 516 if (ret) { 517 dev_err(&pdev->dev, "Failed to assert the reset line\n"); 518 return ret; 519 } 520 521 usleep_range(3000, 5000); 522 523 ret = reset_control_deassert(rstc); 524 if (ret) { 525 dev_err(&pdev->dev, "Failed to deassert the reset line\n"); 526 return ret; 527 } 528 529 /* 530 * In case of the DCMIPP has only 1 clock (such as on MP13), the 531 * clock might not be named. 532 */ 533 kclk = devm_clk_get(&pdev->dev, 534 dcmipp->pipe_cfg->needs_mclk ? "kclk" : NULL); 535 if (IS_ERR(kclk)) 536 return dev_err_probe(&pdev->dev, PTR_ERR(kclk), 537 "Unable to get kclk\n"); 538 dcmipp->kclk = kclk; 539 540 if (dcmipp->pipe_cfg->needs_mclk) { 541 mclk = devm_clk_get(&pdev->dev, "mclk"); 542 if (IS_ERR(mclk)) 543 return dev_err_probe(&pdev->dev, PTR_ERR(mclk), 544 "Unable to get mclk\n"); 545 dcmipp->mclk = mclk; 546 } 547 548 dcmipp->entity = devm_kcalloc(&pdev->dev, dcmipp->pipe_cfg->num_ents, 549 sizeof(*dcmipp->entity), GFP_KERNEL); 550 if (!dcmipp->entity) 551 return -ENOMEM; 552 553 /* Register the v4l2 struct */ 554 ret = v4l2_device_register(&pdev->dev, &dcmipp->v4l2_dev); 555 if (ret) { 556 dev_err(&pdev->dev, 557 "v4l2 device register failed (err=%d)\n", ret); 558 return ret; 559 } 560 561 /* Link the media device within the v4l2_device */ 562 dcmipp->v4l2_dev.mdev = &dcmipp->mdev; 563 564 /* Initialize media device */ 565 strscpy(dcmipp->mdev.model, DCMIPP_MDEV_MODEL_NAME, 566 sizeof(dcmipp->mdev.model)); 567 dcmipp->mdev.hw_revision = pipe_cfg->hw_revision; 568 dcmipp->mdev.dev = &pdev->dev; 569 media_device_init(&dcmipp->mdev); 570 571 /* Initialize subdevs */ 572 ret = dcmipp_create_subdevs(dcmipp); 573 if (ret) { 574 media_device_cleanup(&dcmipp->mdev); 575 v4l2_device_unregister(&dcmipp->v4l2_dev); 576 return ret; 577 } 578 579 pm_runtime_enable(dcmipp->dev); 580 581 dev_info(&pdev->dev, "Probe done"); 582 583 return 0; 584} 585 586static void dcmipp_remove(struct platform_device *pdev) 587{ 588 struct dcmipp_device *dcmipp = platform_get_drvdata(pdev); 589 unsigned int i; 590 591 pm_runtime_disable(&pdev->dev); 592 593 v4l2_async_nf_unregister(&dcmipp->notifier); 594 v4l2_async_nf_cleanup(&dcmipp->notifier); 595 596 for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) 597 dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]); 598 599 media_device_unregister(&dcmipp->mdev); 600 media_device_cleanup(&dcmipp->mdev); 601 602 v4l2_device_unregister(&dcmipp->v4l2_dev); 603} 604 605static int dcmipp_runtime_suspend(struct device *dev) 606{ 607 struct dcmipp_device *dcmipp = dev_get_drvdata(dev); 608 609 clk_disable_unprepare(dcmipp->kclk); 610 clk_disable_unprepare(dcmipp->mclk); 611 612 return 0; 613} 614 615static int dcmipp_runtime_resume(struct device *dev) 616{ 617 struct dcmipp_device *dcmipp = dev_get_drvdata(dev); 618 int ret; 619 620 ret = clk_prepare_enable(dcmipp->mclk); 621 if (ret) { 622 dev_err(dev, "%s: Failed to prepare_enable mclk\n", __func__); 623 return ret; 624 } 625 626 ret = clk_prepare_enable(dcmipp->kclk); 627 if (ret) { 628 clk_disable_unprepare(dcmipp->mclk); 629 dev_err(dev, "%s: Failed to prepare_enable kclk\n", __func__); 630 } 631 632 return ret; 633} 634 635static int dcmipp_suspend(struct device *dev) 636{ 637 /* disable clock */ 638 pm_runtime_force_suspend(dev); 639 640 /* change pinctrl state */ 641 pinctrl_pm_select_sleep_state(dev); 642 643 return 0; 644} 645 646static int dcmipp_resume(struct device *dev) 647{ 648 /* restore pinctl default state */ 649 pinctrl_pm_select_default_state(dev); 650 651 /* clock enable */ 652 pm_runtime_force_resume(dev); 653 654 return 0; 655} 656 657static const struct dev_pm_ops dcmipp_pm_ops = { 658 SYSTEM_SLEEP_PM_OPS(dcmipp_suspend, dcmipp_resume) 659 RUNTIME_PM_OPS(dcmipp_runtime_suspend, dcmipp_runtime_resume, NULL) 660}; 661 662static struct platform_driver dcmipp_pdrv = { 663 .probe = dcmipp_probe, 664 .remove = dcmipp_remove, 665 .driver = { 666 .name = DCMIPP_PDEV_NAME, 667 .of_match_table = dcmipp_of_match, 668 .pm = pm_ptr(&dcmipp_pm_ops), 669 }, 670}; 671 672module_platform_driver(dcmipp_pdrv); 673 674MODULE_AUTHOR("Hugues Fruchet <hugues.fruchet@foss.st.com>"); 675MODULE_AUTHOR("Alain Volmat <alain.volmat@foss.st.com>"); 676MODULE_DESCRIPTION("STMicroelectronics STM32 Digital Camera Memory Interface with Pixel Processor driver"); 677MODULE_LICENSE("GPL");
~/
Read-only file preview.