1 // SPDX-License-Identifier: GPL-2.0-only
3 * Microchip Image Sensor Controller (ISC) Scaler entity support
5 * Copyright (C) 2022 Microchip Technology, Inc.
7 * Author: Eugen Hristev <eugen.hristev@microchip.com>
11 #include <media/media-device.h>
12 #include <media/media-entity.h>
13 #include <media/v4l2-device.h>
14 #include <media/v4l2-subdev.h>
16 #include "microchip-isc-regs.h"
17 #include "microchip-isc.h"
19 static void isc_scaler_prepare_fmt(struct v4l2_mbus_framefmt
*framefmt
)
21 framefmt
->colorspace
= V4L2_COLORSPACE_SRGB
;
22 framefmt
->field
= V4L2_FIELD_NONE
;
23 framefmt
->ycbcr_enc
= V4L2_YCBCR_ENC_DEFAULT
;
24 framefmt
->quantization
= V4L2_QUANTIZATION_DEFAULT
;
25 framefmt
->xfer_func
= V4L2_XFER_FUNC_DEFAULT
;
28 static int isc_scaler_get_fmt(struct v4l2_subdev
*sd
,
29 struct v4l2_subdev_state
*sd_state
,
30 struct v4l2_subdev_format
*format
)
32 struct isc_device
*isc
= container_of(sd
, struct isc_device
, scaler_sd
);
33 struct v4l2_mbus_framefmt
*v4l2_try_fmt
;
35 if (format
->which
== V4L2_SUBDEV_FORMAT_TRY
) {
36 v4l2_try_fmt
= v4l2_subdev_state_get_format(sd_state
,
38 format
->format
= *v4l2_try_fmt
;
43 format
->format
= isc
->scaler_format
[format
->pad
];
48 static int isc_scaler_set_fmt(struct v4l2_subdev
*sd
,
49 struct v4l2_subdev_state
*sd_state
,
50 struct v4l2_subdev_format
*req_fmt
)
52 struct isc_device
*isc
= container_of(sd
, struct isc_device
, scaler_sd
);
53 struct v4l2_mbus_framefmt
*v4l2_try_fmt
;
54 struct isc_format
*fmt
;
57 /* Source format is fixed, we cannot change it */
58 if (req_fmt
->pad
== ISC_SCALER_PAD_SOURCE
) {
59 req_fmt
->format
= isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
];
63 /* There is no limit on the frame size on the sink pad */
64 v4l_bound_align_image(&req_fmt
->format
.width
, 16, UINT_MAX
, 0,
65 &req_fmt
->format
.height
, 16, UINT_MAX
, 0, 0);
67 isc_scaler_prepare_fmt(&req_fmt
->format
);
69 fmt
= isc_find_format_by_code(isc
, req_fmt
->format
.code
, &i
);
72 fmt
= &isc
->formats_list
[0];
74 req_fmt
->format
.code
= fmt
->mbus_code
;
76 if (req_fmt
->which
== V4L2_SUBDEV_FORMAT_TRY
) {
77 v4l2_try_fmt
= v4l2_subdev_state_get_format(sd_state
,
79 *v4l2_try_fmt
= req_fmt
->format
;
80 /* Trying on the sink pad makes the source pad change too */
81 v4l2_try_fmt
= v4l2_subdev_state_get_format(sd_state
,
82 ISC_SCALER_PAD_SOURCE
);
83 *v4l2_try_fmt
= req_fmt
->format
;
85 v4l_bound_align_image(&v4l2_try_fmt
->width
,
86 16, isc
->max_width
, 0,
87 &v4l2_try_fmt
->height
,
88 16, isc
->max_height
, 0, 0);
89 /* if we are just trying, we are done */
93 isc
->scaler_format
[ISC_SCALER_PAD_SINK
] = req_fmt
->format
;
95 /* The source pad is the same as the sink, but we have to crop it */
96 isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
] =
97 isc
->scaler_format
[ISC_SCALER_PAD_SINK
];
99 (&isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
].width
, 16,
101 &isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
].height
, 16,
102 isc
->max_height
, 0, 0);
107 static int isc_scaler_enum_mbus_code(struct v4l2_subdev
*sd
,
108 struct v4l2_subdev_state
*sd_state
,
109 struct v4l2_subdev_mbus_code_enum
*code
)
111 struct isc_device
*isc
= container_of(sd
, struct isc_device
, scaler_sd
);
114 * All formats supported by the ISC are supported by the scaler.
115 * Advertise the formats which the ISC can take as input, as the scaler
116 * entity cropping is part of the PFE module (parallel front end)
118 if (code
->index
< isc
->formats_list_size
) {
119 code
->code
= isc
->formats_list
[code
->index
].mbus_code
;
126 static int isc_scaler_g_sel(struct v4l2_subdev
*sd
,
127 struct v4l2_subdev_state
*sd_state
,
128 struct v4l2_subdev_selection
*sel
)
130 struct isc_device
*isc
= container_of(sd
, struct isc_device
, scaler_sd
);
132 if (sel
->pad
== ISC_SCALER_PAD_SOURCE
)
135 if (sel
->target
!= V4L2_SEL_TGT_CROP_BOUNDS
&&
136 sel
->target
!= V4L2_SEL_TGT_CROP
)
139 sel
->r
.height
= isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
].height
;
140 sel
->r
.width
= isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
].width
;
148 static int isc_scaler_init_state(struct v4l2_subdev
*sd
,
149 struct v4l2_subdev_state
*sd_state
)
151 struct v4l2_mbus_framefmt
*v4l2_try_fmt
=
152 v4l2_subdev_state_get_format(sd_state
, 0);
153 struct v4l2_rect
*try_crop
;
154 struct isc_device
*isc
= container_of(sd
, struct isc_device
, scaler_sd
);
156 *v4l2_try_fmt
= isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
];
158 try_crop
= v4l2_subdev_state_get_crop(sd_state
, 0);
162 try_crop
->width
= v4l2_try_fmt
->width
;
163 try_crop
->height
= v4l2_try_fmt
->height
;
168 static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops
= {
169 .enum_mbus_code
= isc_scaler_enum_mbus_code
,
170 .set_fmt
= isc_scaler_set_fmt
,
171 .get_fmt
= isc_scaler_get_fmt
,
172 .get_selection
= isc_scaler_g_sel
,
175 static const struct media_entity_operations isc_scaler_entity_ops
= {
176 .link_validate
= v4l2_subdev_link_validate
,
179 static const struct v4l2_subdev_ops xisc_scaler_subdev_ops
= {
180 .pad
= &isc_scaler_pad_ops
,
183 static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops
= {
184 .init_state
= isc_scaler_init_state
,
187 int isc_scaler_init(struct isc_device
*isc
)
191 v4l2_subdev_init(&isc
->scaler_sd
, &xisc_scaler_subdev_ops
);
192 isc
->scaler_sd
.internal_ops
= &isc_scaler_internal_ops
;
194 isc
->scaler_sd
.owner
= THIS_MODULE
;
195 isc
->scaler_sd
.dev
= isc
->dev
;
196 snprintf(isc
->scaler_sd
.name
, sizeof(isc
->scaler_sd
.name
),
197 "microchip_isc_scaler");
199 isc
->scaler_sd
.flags
|= V4L2_SUBDEV_FL_HAS_DEVNODE
;
200 isc
->scaler_sd
.entity
.function
= MEDIA_ENT_F_PROC_VIDEO_SCALER
;
201 isc
->scaler_sd
.entity
.ops
= &isc_scaler_entity_ops
;
202 isc
->scaler_pads
[ISC_SCALER_PAD_SINK
].flags
= MEDIA_PAD_FL_SINK
;
203 isc
->scaler_pads
[ISC_SCALER_PAD_SOURCE
].flags
= MEDIA_PAD_FL_SOURCE
;
205 isc_scaler_prepare_fmt(&isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
]);
206 isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
].height
= isc
->max_height
;
207 isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
].width
= isc
->max_width
;
208 isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
].code
=
209 isc
->formats_list
[0].mbus_code
;
211 isc
->scaler_format
[ISC_SCALER_PAD_SINK
] =
212 isc
->scaler_format
[ISC_SCALER_PAD_SOURCE
];
214 ret
= media_entity_pads_init(&isc
->scaler_sd
.entity
,
218 dev_err(isc
->dev
, "scaler sd media entity init failed\n");
222 ret
= v4l2_device_register_subdev(&isc
->v4l2_dev
, &isc
->scaler_sd
);
224 dev_err(isc
->dev
, "scaler sd failed to register subdev\n");
230 EXPORT_SYMBOL_GPL(isc_scaler_init
);
232 int isc_scaler_link(struct isc_device
*isc
)
236 ret
= media_create_pad_link(&isc
->current_subdev
->sd
->entity
,
237 isc
->remote_pad
, &isc
->scaler_sd
.entity
,
239 MEDIA_LNK_FL_ENABLED
|
240 MEDIA_LNK_FL_IMMUTABLE
);
243 dev_err(isc
->dev
, "Failed to create pad link: %s to %s\n",
244 isc
->current_subdev
->sd
->entity
.name
,
245 isc
->scaler_sd
.entity
.name
);
249 dev_dbg(isc
->dev
, "link with %s pad: %d\n",
250 isc
->current_subdev
->sd
->name
, isc
->remote_pad
);
252 ret
= media_create_pad_link(&isc
->scaler_sd
.entity
,
253 ISC_SCALER_PAD_SOURCE
,
254 &isc
->video_dev
.entity
, ISC_PAD_SINK
,
255 MEDIA_LNK_FL_ENABLED
|
256 MEDIA_LNK_FL_IMMUTABLE
);
259 dev_err(isc
->dev
, "Failed to create pad link: %s to %s\n",
260 isc
->scaler_sd
.entity
.name
,
261 isc
->video_dev
.entity
.name
);
265 dev_dbg(isc
->dev
, "link with %s pad: %d\n", isc
->scaler_sd
.name
,
266 ISC_SCALER_PAD_SOURCE
);
270 EXPORT_SYMBOL_GPL(isc_scaler_link
);