diff options
Diffstat (limited to 'drivers/media/v4l2-core')
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-async.c | 8 | ||||
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-cci.c | 9 | ||||
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-common.c | 2 | ||||
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-ctrls-defs.c | 5 | ||||
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-fwnode.c | 1 | ||||
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-ioctl.c | 13 | ||||
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-jpeg.c | 116 | ||||
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-subdev.c | 393 | 
8 files changed, 386 insertions, 161 deletions
| diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 222f01665f7c..ee884a8221fb 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -323,6 +323,13 @@ static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n,  	    sd->entity.function != MEDIA_ENT_F_FLASH)  		return 0; +	if (!n->sd) { +		dev_warn(notifier_dev(n), +			 "not a sub-device notifier, not creating an ancillary link for %s!\n", +			 dev_name(sd->dev)); +		return 0; +	} +  	link = media_create_ancillary_link(&n->sd->entity, &sd->entity);  	return IS_ERR(link) ? PTR_ERR(link) : 0; @@ -965,4 +972,5 @@ module_exit(v4l2_async_exit);  MODULE_AUTHOR("Guennadi Liakhovetski <[email protected]>");  MODULE_AUTHOR("Sakari Ailus <[email protected]>");  MODULE_AUTHOR("Ezequiel Garcia <[email protected]>"); +MODULE_DESCRIPTION("V4L2 asynchronous subdevice registration API");  MODULE_LICENSE("GPL"); diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c index ee3475bed37f..1ff94affbaf3 100644 --- a/drivers/media/v4l2-core/v4l2-cci.c +++ b/drivers/media/v4l2-core/v4l2-cci.c @@ -23,6 +23,15 @@ int cci_read(struct regmap *map, u32 reg, u64 *val, int *err)  	u8 buf[8];  	int ret; +	/* +	 * TODO: Fix smatch. Assign *val to 0 here in order to avoid +	 * failing a smatch check on caller when the caller proceeds to +	 * read *val without initialising it on caller's side. *val is set +	 * to a valid value whenever this function returns 0 but smatch +	 * can't figure that out currently. +	 */ +	*val = 0; +  	if (err && *err)  		return *err; diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 4165c815faef..0a2f4f0d0a07 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -253,6 +253,8 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)  		{ .format = V4L2_PIX_FMT_RGB555,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },  		{ .format = V4L2_PIX_FMT_BGR666,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },  		{ .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, +		{ .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, +		{ .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },  		{ .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },  		{ .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },  		{ .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 8696eb1cdd61..1ea52011247a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -970,6 +970,7 @@ const char *v4l2_ctrl_get_name(u32 id)  	case V4L2_CID_MPEG_VIDEO_LTR_COUNT:			return "LTR Count";  	case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX:		return "Frame LTR Index";  	case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES:		return "Use LTR Frames"; +	case V4L2_CID_MPEG_VIDEO_AVERAGE_QP:			return "Average QP Value";  	case V4L2_CID_FWHT_I_FRAME_QP:				return "FWHT I-Frame QP Value";  	case V4L2_CID_FWHT_P_FRAME_QP:				return "FWHT P-Frame QP Value"; @@ -1507,6 +1508,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,  		*max = 0xffffffffffffLL;  		*step = 1;  		break; +	case V4L2_CID_MPEG_VIDEO_AVERAGE_QP: +		*type = V4L2_CTRL_TYPE_INTEGER; +		*flags |= V4L2_CTRL_FLAG_READ_ONLY; +		break;  	case V4L2_CID_PIXEL_RATE:  		*type = V4L2_CTRL_TYPE_INTEGER64;  		*flags |= V4L2_CTRL_FLAG_READ_ONLY; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 89c7192148df..f19c8adf2c61 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1251,6 +1251,7 @@ out_cleanup:  }  EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor); +MODULE_DESCRIPTION("V4L2 fwnode binding parsing library");  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Sakari Ailus <[email protected]>");  MODULE_AUTHOR("Sylwester Nawrocki <[email protected]>"); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 4c76d17b4629..5eb4d797d259 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1307,6 +1307,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)  	case V4L2_PIX_FMT_RGBX1010102:	descr = "32-bit RGBX 10-10-10-2"; break;  	case V4L2_PIX_FMT_RGBA1010102:	descr = "32-bit RGBA 10-10-10-2"; break;  	case V4L2_PIX_FMT_ARGB2101010:	descr = "32-bit ARGB 2-10-10-10"; break; +	case V4L2_PIX_FMT_BGR48:	descr = "48-bit BGR 16-16-16"; break; +	case V4L2_PIX_FMT_RGB48:	descr = "48-bit RGB 16-16-16"; break;  	case V4L2_PIX_FMT_BGR48_12:	descr = "12-bit Depth BGR"; break;  	case V4L2_PIX_FMT_ABGR64_12:	descr = "12-bit Depth BGRA"; break;  	case V4L2_PIX_FMT_GREY:		descr = "8-bit Greyscale"; break; @@ -1463,6 +1465,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)  	case V4L2_PIX_FMT_Y210:		descr = "10-bit YUYV Packed"; break;  	case V4L2_PIX_FMT_Y212:		descr = "12-bit YUYV Packed"; break;  	case V4L2_PIX_FMT_Y216:		descr = "16-bit YUYV Packed"; break; +	case V4L2_META_FMT_RPI_BE_CFG:	descr = "RPi PiSP BE Config format"; break;  	case V4L2_META_FMT_GENERIC_8:	descr = "8-bit Generic Metadata"; break;  	case V4L2_META_FMT_GENERIC_CSI2_10:	descr = "8-bit Generic Meta, 10b CSI-2"; break;  	case V4L2_META_FMT_GENERIC_CSI2_12:	descr = "8-bit Generic Meta, 12b CSI-2"; break; @@ -1529,6 +1532,16 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)  		case V4L2_PIX_FMT_MT2110T:	descr = "Mediatek 10bit Tile Mode"; break;  		case V4L2_PIX_FMT_MT2110R:	descr = "Mediatek 10bit Raster Mode"; break;  		case V4L2_PIX_FMT_HEXTILE:	descr = "Hextile Compressed Format"; break; +		case V4L2_PIX_FMT_PISP_COMP1_RGGB: descr = "PiSP 8b RGRG/GBGB mode1 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP1_GRBG: descr = "PiSP 8b GRGR/BGBG mode1 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP1_GBRG: descr = "PiSP 8b GBGB/RGRG mode1 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP1_BGGR: descr = "PiSP 8b BGBG/GRGR mode1 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP1_MONO: descr = "PiSP 8b monochrome mode1 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP2_RGGB: descr = "PiSP 8b RGRG/GBGB mode2 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP2_GRBG: descr = "PiSP 8b GRGR/BGBG mode2 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP2_GBRG: descr = "PiSP 8b GBGB/RGRG mode2 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP2_BGGR: descr = "PiSP 8b BGBG/GRGR mode2 compr"; break; +		case V4L2_PIX_FMT_PISP_COMP2_MONO: descr = "PiSP 8b monochrome mode2 compr"; break;  		default:  			if (fmt->description[0])  				return; diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c index 94435a7b6816..b8bece739d07 100644 --- a/drivers/media/v4l2-core/v4l2-jpeg.c +++ b/drivers/media/v4l2-core/v4l2-jpeg.c @@ -52,6 +52,122 @@ MODULE_LICENSE("GPL");  #define COM	0xfffe	/* comment */  #define TEM	0xff01	/* temporary */ +/* Luma and chroma qp tables to achieve 50% compression quality + * This is as per example in Annex K.1 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_qt[V4L2_JPEG_PIXELS_IN_BLOCK] = { +	16, 11, 10, 16, 24, 40, 51, 61, +	12, 12, 14, 19, 26, 58, 60, 55, +	14, 13, 16, 24, 40, 57, 69, 56, +	14, 17, 22, 29, 51, 87, 80, 62, +	18, 22, 37, 56, 68, 109, 103, 77, +	24, 35, 55, 64, 81, 104, 113, 92, +	49, 64, 78, 87, 103, 121, 120, 101, +	72, 92, 95, 98, 112, 100, 103, 99 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_qt); + +const u8 v4l2_jpeg_ref_table_chroma_qt[V4L2_JPEG_PIXELS_IN_BLOCK] = { +	17, 18, 24, 47, 99, 99, 99, 99, +	18, 21, 26, 66, 99, 99, 99, 99, +	24, 26, 56, 99, 99, 99, 99, 99, +	47, 66, 99, 99, 99, 99, 99, 99, +	99, 99, 99, 99, 99, 99, 99, 99, +	99, 99, 99, 99, 99, 99, 99, 99, +	99, 99, 99, 99, 99, 99, 99, 99, +	99, 99, 99, 99, 99, 99, 99, 99 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_qt); + +/* Zigzag scan pattern indexes */ +const u8 v4l2_jpeg_zigzag_scan_index[V4L2_JPEG_PIXELS_IN_BLOCK] = { +	0,   1,  8, 16,  9,  2,  3, 10, +	17, 24, 32, 25, 18, 11,  4,  5, +	12, 19, 26, 33, 40, 48, 41, 34, +	27, 20, 13,  6,  7, 14, 21, 28, +	35, 42, 49, 56, 57, 50, 43, 36, +	29, 22, 15, 23, 30, 37, 44, 51, +	58, 59, 52, 45, 38, 31, 39, 46, +	53, 60, 61, 54, 47, 55, 62, 63 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_zigzag_scan_index); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the luminance DC + * coefficient differences. The table represents Table K.3 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN] = { +	0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, +	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_dc_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the luminance AC + * coefficients. The table represents Table K.5 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN] = { +	0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, +	0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, +	0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, +	0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, +	0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, +	0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, +	0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, +	0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, +	0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, +	0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, +	0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, +	0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, +	0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, +	0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, +	0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_ac_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an interchange format JPEG + * stream or an abbreviated format table specification data stream. + * Specifies the huffman table used for encoding the chrominance DC coefficient differences. + * The table represents Table K.4 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_chroma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN] = { +	0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +	0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_dc_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the chrominance + * AC coefficients. The table represents Table K.6 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_chroma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN] = { +	0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, +	0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, +	0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, +	0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, +	0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, +	0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, +	0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, +	0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, +	0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, +	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, +	0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, +	0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, +	0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, +	0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, +	0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_ac_ht); +  /**   * struct jpeg_stream - JPEG byte stream   * @curr: current position in stream diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 8470d6eda9a3..7c5812d55315 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -148,6 +148,23 @@ static int subdev_close(struct file *file)  }  #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) +	if (!IS_ERR_OR_NULL(sd->privacy_led)) +		led_set_brightness(sd->privacy_led, +				   sd->privacy_led->max_brightness); +#endif +} + +static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) +	if (!IS_ERR_OR_NULL(sd->privacy_led)) +		led_set_brightness(sd->privacy_led, 0); +#endif +} +  static inline int check_which(u32 which)  {  	if (which != V4L2_SUBDEV_FORMAT_TRY && @@ -434,12 +451,8 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)  	 * The .s_stream() operation must never be called to start or stop an  	 * already started or stopped subdev. Catch offenders but don't return  	 * an error yet to avoid regressions. -	 * -	 * As .s_stream() is mutually exclusive with the .enable_streams() and -	 * .disable_streams() operation, we can use the enabled_streams field -	 * to store the subdev streaming state.  	 */ -	if (WARN_ON(!!sd->enabled_streams == !!enable)) +	if (WARN_ON(sd->s_stream_enabled == !!enable))  		return 0;  	ret = sd->ops->video->s_stream(sd, enable); @@ -450,17 +463,12 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)  	}  	if (!ret) { -		sd->enabled_streams = enable ? BIT(0) : 0; +		sd->s_stream_enabled = enable; -#if IS_REACHABLE(CONFIG_LEDS_CLASS) -		if (!IS_ERR_OR_NULL(sd->privacy_led)) { -			if (enable) -				led_set_brightness(sd->privacy_led, -						   sd->privacy_led->max_brightness); -			else -				led_set_brightness(sd->privacy_led, 0); -		} -#endif +		if (enable) +			v4l2_subdev_enable_privacy_led(sd); +		else +			v4l2_subdev_disable_privacy_led(sd);  	}  	return ret; @@ -1261,14 +1269,6 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream,  	struct v4l2_subdev *sd;  	int ret; -	if (!is_media_entity_v4l2_subdev(pad->entity)) { -		WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, -		     "Driver bug! Wrong media entity type 0x%08x, entity %s\n", -		     pad->entity->function, pad->entity->name); - -		return -EINVAL; -	} -  	sd = media_entity_to_v4l2_subdev(pad->entity);  	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -1576,6 +1576,33 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name,  				struct lock_class_key *key)  {  	struct v4l2_subdev_state *state; +	struct device *dev = sd->dev; +	bool has_disable_streams; +	bool has_enable_streams; +	bool has_s_stream; + +	/* Check that the subdevice implements the required features */ + +	has_s_stream = v4l2_subdev_has_op(sd, video, s_stream); +	has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams); +	has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams); + +	if (has_enable_streams != has_disable_streams) { +		dev_err(dev, +			"subdev '%s' must implement both or neither of .enable_streams() and .disable_streams()\n", +			sd->name); +		return -EINVAL; +	} + +	if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { +		if (has_s_stream && !has_enable_streams) { +			dev_err(dev, +				"subdev '%s' must implement .enable/disable_streams()\n", +				sd->name); + +			return -EINVAL; +		} +	}  	state = __v4l2_subdev_state_alloc(sd, name, key);  	if (IS_ERR(state)) @@ -2120,43 +2147,55 @@ out:  }  EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate); -static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, -					       u64 streams_mask) +static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd, +					struct v4l2_subdev_state *state, +					u32 pad, u64 streams_mask, +					u64 *found_streams, +					u64 *enabled_streams)  { -	struct device *dev = sd->entity.graph_obj.mdev->dev; -	unsigned int i; -	int ret; +	if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { +		*found_streams = BIT_ULL(0); +		*enabled_streams = +			(sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0; +		return; +	} -	/* -	 * The subdev doesn't implement pad-based stream enable, fall back -	 * on the .s_stream() operation. This can only be done for subdevs that -	 * have a single source pad, as sd->enabled_streams is global to the -	 * subdev. -	 */ -	if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) -		return -EOPNOTSUPP; +	*found_streams = 0; +	*enabled_streams = 0; -	for (i = 0; i < sd->entity.num_pads; ++i) { -		if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) -			return -EOPNOTSUPP; -	} +	for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { +		const struct v4l2_subdev_stream_config *cfg = +			&state->stream_configs.configs[i]; -	if (sd->enabled_streams & streams_mask) { -		dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n", -			streams_mask, sd->entity.name, pad); -		return -EALREADY; +		if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) +			continue; + +		*found_streams |= BIT_ULL(cfg->stream); +		if (cfg->enabled) +			*enabled_streams |= BIT_ULL(cfg->stream);  	} +} -	/* Start streaming when the first streams are enabled. */ -	if (!sd->enabled_streams) { -		ret = v4l2_subdev_call(sd, video, s_stream, 1); -		if (ret) -			return ret; +static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, +					    struct v4l2_subdev_state *state, +					    u32 pad, u64 streams_mask, +					    bool enabled) +{ +	if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { +		if (enabled) +			sd->enabled_pads |= BIT_ULL(pad); +		else +			sd->enabled_pads &= ~BIT_ULL(pad); +		return;  	} -	sd->enabled_streams |= streams_mask; +	for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { +		struct v4l2_subdev_stream_config *cfg = +			&state->stream_configs.configs[i]; -	return 0; +		if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) +			cfg->enabled = enabled; +	}  }  int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, @@ -2164,44 +2203,44 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,  {  	struct device *dev = sd->entity.graph_obj.mdev->dev;  	struct v4l2_subdev_state *state; -	u64 found_streams = 0; -	unsigned int i; +	bool already_streaming; +	u64 enabled_streams; +	u64 found_streams; +	bool use_s_stream;  	int ret;  	/* A few basic sanity checks first. */  	if (pad >= sd->entity.num_pads)  		return -EINVAL; +	if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) +		return -EOPNOTSUPP; + +	/* +	 * We use a 64-bit bitmask for tracking enabled pads, so only subdevices +	 * with 64 pads or less can be supported. +	 */ +	if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) +		return -EOPNOTSUPP; +  	if (!streams_mask)  		return 0;  	/* Fallback on .s_stream() if .enable_streams() isn't available. */ -	if (!sd->ops->pad || !sd->ops->pad->enable_streams) -		return v4l2_subdev_enable_streams_fallback(sd, pad, -							   streams_mask); +	use_s_stream = !v4l2_subdev_has_op(sd, pad, enable_streams); -	state = v4l2_subdev_lock_and_get_active_state(sd); +	if (!use_s_stream) +		state = v4l2_subdev_lock_and_get_active_state(sd); +	else +		state = NULL;  	/*  	 * Verify that the requested streams exist and that they are not  	 * already enabled.  	 */ -	for (i = 0; i < state->stream_configs.num_configs; ++i) { -		struct v4l2_subdev_stream_config *cfg = -			&state->stream_configs.configs[i]; - -		if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) -			continue; -		found_streams |= BIT_ULL(cfg->stream); - -		if (cfg->enabled) { -			dev_dbg(dev, "stream %u already enabled on %s:%u\n", -				cfg->stream, sd->entity.name, pad); -			ret = -EALREADY; -			goto done; -		} -	} +	v4l2_subdev_collect_streams(sd, state, pad, streams_mask, +				    &found_streams, &enabled_streams);  	if (found_streams != streams_mask) {  		dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2210,11 +2249,29 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,  		goto done;  	} +	if (enabled_streams) { +		dev_dbg(dev, "streams 0x%llx already enabled on %s:%u\n", +			enabled_streams, sd->entity.name, pad); +		ret = -EALREADY; +		goto done; +	} +  	dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask); -	/* Call the .enable_streams() operation. */ -	ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, -			       streams_mask); +	already_streaming = v4l2_subdev_is_streaming(sd); + +	if (!use_s_stream) { +		/* Call the .enable_streams() operation. */ +		ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, +				       streams_mask); +	} else { +		/* Start streaming when the first pad is enabled. */ +		if (!already_streaming) +			ret = v4l2_subdev_call(sd, video, s_stream, 1); +		else +			ret = 0; +	} +  	if (ret) {  		dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad,  			streams_mask, ret); @@ -2222,103 +2279,68 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,  	}  	/* Mark the streams as enabled. */ -	for (i = 0; i < state->stream_configs.num_configs; ++i) { -		struct v4l2_subdev_stream_config *cfg = -			&state->stream_configs.configs[i]; +	v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true); -		if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) -			cfg->enabled = true; -	} +	/* +	 * TODO: When all the drivers have been changed to use +	 * v4l2_subdev_enable_streams() and v4l2_subdev_disable_streams(), +	 * instead of calling .s_stream() operation directly, we can remove +	 * the privacy LED handling from call_s_stream() and do it here +	 * for all cases. +	 */ +	if (!use_s_stream && !already_streaming) +		v4l2_subdev_enable_privacy_led(sd);  done: -	v4l2_subdev_unlock_state(state); +	if (!use_s_stream) +		v4l2_subdev_unlock_state(state);  	return ret;  }  EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams); -static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, -						u64 streams_mask) -{ -	struct device *dev = sd->entity.graph_obj.mdev->dev; -	unsigned int i; -	int ret; - -	/* -	 * If the subdev doesn't implement pad-based stream enable, fall  back -	 * on the .s_stream() operation. This can only be done for subdevs that -	 * have a single source pad, as sd->enabled_streams is global to the -	 * subdev. -	 */ -	if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) -		return -EOPNOTSUPP; - -	for (i = 0; i < sd->entity.num_pads; ++i) { -		if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) -			return -EOPNOTSUPP; -	} - -	if ((sd->enabled_streams & streams_mask) != streams_mask) { -		dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n", -			streams_mask, sd->entity.name, pad); -		return -EALREADY; -	} - -	/* Stop streaming when the last streams are disabled. */ -	if (!(sd->enabled_streams & ~streams_mask)) { -		ret = v4l2_subdev_call(sd, video, s_stream, 0); -		if (ret) -			return ret; -	} - -	sd->enabled_streams &= ~streams_mask; - -	return 0; -} -  int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,  				u64 streams_mask)  {  	struct device *dev = sd->entity.graph_obj.mdev->dev;  	struct v4l2_subdev_state *state; -	u64 found_streams = 0; -	unsigned int i; +	u64 enabled_streams; +	u64 found_streams; +	bool use_s_stream;  	int ret;  	/* A few basic sanity checks first. */  	if (pad >= sd->entity.num_pads)  		return -EINVAL; +	if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) +		return -EOPNOTSUPP; + +	/* +	 * We use a 64-bit bitmask for tracking enabled pads, so only subdevices +	 * with 64 pads or less can be supported. +	 */ +	if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) +		return -EOPNOTSUPP; +  	if (!streams_mask)  		return 0;  	/* Fallback on .s_stream() if .disable_streams() isn't available. */ -	if (!sd->ops->pad || !sd->ops->pad->disable_streams) -		return v4l2_subdev_disable_streams_fallback(sd, pad, -							    streams_mask); +	use_s_stream = !v4l2_subdev_has_op(sd, pad, disable_streams); -	state = v4l2_subdev_lock_and_get_active_state(sd); +	if (!use_s_stream) +		state = v4l2_subdev_lock_and_get_active_state(sd); +	else +		state = NULL;  	/*  	 * Verify that the requested streams exist and that they are not  	 * already disabled.  	 */ -	for (i = 0; i < state->stream_configs.num_configs; ++i) { -		struct v4l2_subdev_stream_config *cfg = -			&state->stream_configs.configs[i]; -		if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) -			continue; - -		found_streams |= BIT_ULL(cfg->stream); - -		if (!cfg->enabled) { -			dev_dbg(dev, "stream %u already disabled on %s:%u\n", -				cfg->stream, sd->entity.name, pad); -			ret = -EALREADY; -			goto done; -		} -	} +	v4l2_subdev_collect_streams(sd, state, pad, streams_mask, +				    &found_streams, &enabled_streams);  	if (found_streams != streams_mask) {  		dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2327,28 +2349,43 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,  		goto done;  	} +	if (enabled_streams != streams_mask) { +		dev_dbg(dev, "streams 0x%llx already disabled on %s:%u\n", +			streams_mask & ~enabled_streams, sd->entity.name, pad); +		ret = -EALREADY; +		goto done; +	} +  	dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); -	/* Call the .disable_streams() operation. */ -	ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, -			       streams_mask); +	if (!use_s_stream) { +		/* Call the .disable_streams() operation. */ +		ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, +				       streams_mask); +	} else { +		/* Stop streaming when the last streams are disabled. */ + +		if (!(sd->enabled_pads & ~BIT_ULL(pad))) +			ret = v4l2_subdev_call(sd, video, s_stream, 0); +		else +			ret = 0; +	} +  	if (ret) {  		dev_dbg(dev, "disable streams %u:%#llx failed: %d\n", pad,  			streams_mask, ret);  		goto done;  	} -	/* Mark the streams as disabled. */ -	for (i = 0; i < state->stream_configs.num_configs; ++i) { -		struct v4l2_subdev_stream_config *cfg = -			&state->stream_configs.configs[i]; - -		if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) -			cfg->enabled = false; -	} +	v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false);  done: -	v4l2_subdev_unlock_state(state); +	if (!use_s_stream) { +		if (!v4l2_subdev_is_streaming(sd)) +			v4l2_subdev_disable_privacy_led(sd); + +		v4l2_subdev_unlock_state(state); +	}  	return ret;  } @@ -2377,15 +2414,24 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable)  	if (WARN_ON(pad_index == -1))  		return -EINVAL; -	/* -	 * As there's a single source pad, just collect all the source streams. -	 */ -	state = v4l2_subdev_lock_and_get_active_state(sd); +	if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { +		/* +		 * As there's a single source pad, just collect all the source +		 * streams. +		 */ +		state = v4l2_subdev_lock_and_get_active_state(sd); -	for_each_active_route(&state->routing, route) -		source_mask |= BIT_ULL(route->source_stream); +		for_each_active_route(&state->routing, route) +			source_mask |= BIT_ULL(route->source_stream); -	v4l2_subdev_unlock_state(state); +		v4l2_subdev_unlock_state(state); +	} else { +		/* +		 * For non-streams subdevices, there's a single implicit stream +		 * per pad. +		 */ +		source_mask = BIT_ULL(0); +	}  	if (enable)  		return v4l2_subdev_enable_streams(sd, pad_index, source_mask); @@ -2427,6 +2473,31 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd,  }  EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); +bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd) +{ +	struct v4l2_subdev_state *state; + +	if (!v4l2_subdev_has_op(sd, pad, enable_streams)) +		return sd->s_stream_enabled; + +	if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) +		return !!sd->enabled_pads; + +	state = v4l2_subdev_get_locked_active_state(sd); + +	for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { +		const struct v4l2_subdev_stream_config *cfg; + +		cfg = &state->stream_configs.configs[i]; + +		if (cfg->enabled) +			return true; +	} + +	return false; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_is_streaming); +  int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd)  {  #if IS_REACHABLE(CONFIG_LEDS_CLASS) |