File Index
Files and directories
Sign in
FILE

dcmipp-core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c 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.