dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] drm: Refactor plane size calculation by core helper functions
@ 2023-07-20  2:19 Carlos Eduardo Gallo Filho
  2023-07-20  2:19 ` [PATCH v2 1/2] drm: Remove plane hsub/vsub alignment requirement for core helpers Carlos Eduardo Gallo Filho
  2023-07-20  2:19 ` [PATCH v2 2/2] drm: Replace drm_framebuffer plane size functions with its equivalents Carlos Eduardo Gallo Filho
  0 siblings, 2 replies; 5+ messages in thread
From: Carlos Eduardo Gallo Filho @ 2023-07-20  2:19 UTC (permalink / raw)
  To: André Almeida, Maira Canal, Arthur Grillo, Tales Aparecida,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Jani Nikula, Joonas Lahtinen,
	Rodrigo Vivi, Tvrtko Ursulin, Juha-Pekka Heikkila,
	Ville Syrjälä,
	Matt Roper, Clint Taylor, Carlos Eduardo Gallo Filho,
	Niranjana Vishwanathapura
  Cc: intel-gfx, dri-devel

There's duplicated functions on drm that do the same job of calculating
the size of planes from a drm_format_info and the size of its first
plane. So this patchset throw away the more specific version intended
to be used from a given framebuffer and make the generic version way
more portable against the drivers.

Thanks,
Carlos

---
v1 -> v2: https://lore.kernel.org/dri-devel/20230627182239.15676-1-gcarlos@disroot.org/
  - New patch "[PATCH v2 1/2] drm: Remove plane hsub/vsub alignment requirement
    for core helpers".

---
Carlos Eduardo Gallo Filho (2):
  drm: Remove plane hsub/vsub alignment requirement for core helpers
  drm: Replace drm_framebuffer plane size functions with its equivalents

 drivers/gpu/drm/drm_framebuffer.c       | 64 ++-----------------------
 drivers/gpu/drm/i915/display/intel_fb.c |  2 +-
 include/drm/drm_fourcc.h                |  5 +-
 include/drm/drm_framebuffer.h           |  5 --
 4 files changed, 8 insertions(+), 68 deletions(-)

-- 
2.41.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/2] drm: Remove plane hsub/vsub alignment requirement for core helpers
  2023-07-20  2:19 [PATCH v2 0/2] drm: Refactor plane size calculation by core helper functions Carlos Eduardo Gallo Filho
@ 2023-07-20  2:19 ` Carlos Eduardo Gallo Filho
  2023-07-20  2:19 ` [PATCH v2 2/2] drm: Replace drm_framebuffer plane size functions with its equivalents Carlos Eduardo Gallo Filho
  1 sibling, 0 replies; 5+ messages in thread
From: Carlos Eduardo Gallo Filho @ 2023-07-20  2:19 UTC (permalink / raw)
  To: André Almeida, Maira Canal, Arthur Grillo, Tales Aparecida,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Jani Nikula, Joonas Lahtinen,
	Rodrigo Vivi, Tvrtko Ursulin, Juha-Pekka Heikkila,
	Ville Syrjälä,
	Matt Roper, Clint Taylor, Carlos Eduardo Gallo Filho,
	Niranjana Vishwanathapura
  Cc: intel-gfx, dri-devel

The drm_format_info_plane_{height,width} functions was implemented using
regular division for the plane size calculation, which cause issues [1][2]
when used on contexts where the dimensions are misaligned with relation
to the subsampling factors. So, replace the regular division by the
DIV_ROUND_UP macro.

This allows these functions to be used in more drivers, making further
work to bring more core presence on them possible.

[1] http://patchwork.freedesktop.org/patch/msgid/20170321181218.10042-3-ville.syrjala@linux.intel.com
[2] https://patchwork.freedesktop.org/patch/msgid/20211026225105.2783797-2-imre.deak@intel.com

Signed-off-by: Carlos Eduardo Gallo Filho <gcarlos@disroot.org>
---
 include/drm/drm_fourcc.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h
index 532ae78ca747..ccf91daa4307 100644
--- a/include/drm/drm_fourcc.h
+++ b/include/drm/drm_fourcc.h
@@ -22,6 +22,7 @@
 #ifndef __DRM_FOURCC_H__
 #define __DRM_FOURCC_H__
 
+#include <linux/math.h>
 #include <linux/types.h>
 #include <uapi/drm/drm_fourcc.h>
 
@@ -279,7 +280,7 @@ int drm_format_info_plane_width(const struct drm_format_info *info, int width,
 	if (plane == 0)
 		return width;
 
-	return width / info->hsub;
+	return DIV_ROUND_UP(width, info->hsub);
 }
 
 /**
@@ -301,7 +302,7 @@ int drm_format_info_plane_height(const struct drm_format_info *info, int height,
 	if (plane == 0)
 		return height;
 
-	return height / info->vsub;
+	return DIV_ROUND_UP(height, info->vsub);
 }
 
 const struct drm_format_info *__drm_format_info(u32 format);
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/2] drm: Replace drm_framebuffer plane size functions with its equivalents
  2023-07-20  2:19 [PATCH v2 0/2] drm: Refactor plane size calculation by core helper functions Carlos Eduardo Gallo Filho
  2023-07-20  2:19 ` [PATCH v2 1/2] drm: Remove plane hsub/vsub alignment requirement for core helpers Carlos Eduardo Gallo Filho
@ 2023-07-20  2:19 ` Carlos Eduardo Gallo Filho
  1 sibling, 0 replies; 5+ messages in thread
From: Carlos Eduardo Gallo Filho @ 2023-07-20  2:19 UTC (permalink / raw)
  To: André Almeida, Maira Canal, Arthur Grillo, Tales Aparecida,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Jani Nikula, Joonas Lahtinen,
	Rodrigo Vivi, Tvrtko Ursulin, Juha-Pekka Heikkila,
	Ville Syrjälä,
	Matt Roper, Clint Taylor, Carlos Eduardo Gallo Filho,
	Niranjana Vishwanathapura
  Cc: intel-gfx, dri-devel

The functions drm_framebuffer_plane_{width,height} and
fb_plane_{width,height} do exactly the same job of its
equivalents drm_format_info_plane_{width,height} from drm_fourcc.

The only reason to have these functions on drm_framebuffer
would be if they would added a abstraction layer to call it just
passing a drm_framebuffer pointer and the desired plane index,
which is not the case, where these functions actually implements
just part of it. In the actual implementation, every call to both
drm_framebuffer_plane_{width,height} and fb_plane_{width,height} should
pass some drm_framebuffer attribute, which is the same as calling the
drm_format_info_plane_{width,height} functions.

The drm_format_info_pane_{width,height} functions are much more
consistent in both its implementation and its location on code. The
kind of calculation that they do is intrinsically derivated from the
drm_format_info struct and has not to do with drm_framebuffer, except
by the potential motivation described above, which is still not a good
justification to have drm_framebuffer functions to calculate it.

So, replace each drm_framebuffer_plane_{width,height} and
fb_plane_{width,height} call to drm_format_info_plane_{width,height}
and remove them.

Signed-off-by: Carlos Eduardo Gallo Filho <gcarlos@disroot.org>
---
 drivers/gpu/drm/drm_framebuffer.c       | 64 ++-----------------------
 drivers/gpu/drm/i915/display/intel_fb.c |  2 +-
 include/drm/drm_framebuffer.h           |  5 --
 3 files changed, 5 insertions(+), 66 deletions(-)

diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index aff3746dedfb..efed4cd7965e 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -151,24 +151,6 @@ int drm_mode_addfb_ioctl(struct drm_device *dev,
 	return drm_mode_addfb(dev, data, file_priv);
 }
 
-static int fb_plane_width(int width,
-			  const struct drm_format_info *format, int plane)
-{
-	if (plane == 0)
-		return width;
-
-	return DIV_ROUND_UP(width, format->hsub);
-}
-
-static int fb_plane_height(int height,
-			   const struct drm_format_info *format, int plane)
-{
-	if (plane == 0)
-		return height;
-
-	return DIV_ROUND_UP(height, format->vsub);
-}
-
 static int framebuffer_check(struct drm_device *dev,
 			     const struct drm_mode_fb_cmd2 *r)
 {
@@ -196,8 +178,8 @@ static int framebuffer_check(struct drm_device *dev,
 	info = drm_get_format_info(dev, r);
 
 	for (i = 0; i < info->num_planes; i++) {
-		unsigned int width = fb_plane_width(r->width, info, i);
-		unsigned int height = fb_plane_height(r->height, info, i);
+		unsigned int width = drm_format_info_plane_width(info, r->width, i);
+		unsigned int height = drm_format_info_plane_height(info, r->height, i);
 		unsigned int block_size = info->char_per_block[i];
 		u64 min_pitch = drm_format_info_min_pitch(info, i, width);
 
@@ -1136,44 +1118,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);
 
-/**
- * drm_framebuffer_plane_width - width of the plane given the first plane
- * @width: width of the first plane
- * @fb: the framebuffer
- * @plane: plane index
- *
- * Returns:
- * The width of @plane, given that the width of the first plane is @width.
- */
-int drm_framebuffer_plane_width(int width,
-				const struct drm_framebuffer *fb, int plane)
-{
-	if (plane >= fb->format->num_planes)
-		return 0;
-
-	return fb_plane_width(width, fb->format, plane);
-}
-EXPORT_SYMBOL(drm_framebuffer_plane_width);
-
-/**
- * drm_framebuffer_plane_height - height of the plane given the first plane
- * @height: height of the first plane
- * @fb: the framebuffer
- * @plane: plane index
- *
- * Returns:
- * The height of @plane, given that the height of the first plane is @height.
- */
-int drm_framebuffer_plane_height(int height,
-				 const struct drm_framebuffer *fb, int plane)
-{
-	if (plane >= fb->format->num_planes)
-		return 0;
-
-	return fb_plane_height(height, fb->format, plane);
-}
-EXPORT_SYMBOL(drm_framebuffer_plane_height);
-
 void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
 				const struct drm_framebuffer *fb)
 {
@@ -1189,8 +1133,8 @@ void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
 
 	for (i = 0; i < fb->format->num_planes; i++) {
 		drm_printf_indent(p, indent + 1, "size[%u]=%dx%d\n", i,
-				  drm_framebuffer_plane_width(fb->width, fb, i),
-				  drm_framebuffer_plane_height(fb->height, fb, i));
+				  drm_format_info_plane_width(fb->format, fb->width, i),
+				  drm_format_info_plane_height(fb->format, fb->height, i));
 		drm_printf_indent(p, indent + 1, "pitch[%u]=%u\n", i, fb->pitches[i]);
 		drm_printf_indent(p, indent + 1, "offset[%u]=%u\n", i, fb->offsets[i]);
 		drm_printf_indent(p, indent + 1, "obj[%u]:%s\n", i,
diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c
index 446bbf7986b6..5de2453ff7a3 100644
--- a/drivers/gpu/drm/i915/display/intel_fb.c
+++ b/drivers/gpu/drm/i915/display/intel_fb.c
@@ -1113,7 +1113,7 @@ static int intel_fb_offset_to_xy(int *x, int *y,
 		return -EINVAL;
 	}
 
-	height = drm_framebuffer_plane_height(fb->height, fb, color_plane);
+	height = drm_format_info_plane_height(fb->format, fb->height, color_plane);
 	height = ALIGN(height, intel_tile_height(fb, color_plane));
 
 	/* Catch potential overflows early */
diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h
index 0dcc07b68654..80ece7b6dd9b 100644
--- a/include/drm/drm_framebuffer.h
+++ b/include/drm/drm_framebuffer.h
@@ -292,11 +292,6 @@ static inline void drm_framebuffer_assign(struct drm_framebuffer **p,
 	     &fb->head != (&(dev)->mode_config.fb_list);			\
 	     fb = list_next_entry(fb, head))
 
-int drm_framebuffer_plane_width(int width,
-				const struct drm_framebuffer *fb, int plane);
-int drm_framebuffer_plane_height(int height,
-				 const struct drm_framebuffer *fb, int plane);
-
 /**
  * struct drm_afbc_framebuffer - a special afbc frame buffer object
  *
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 2/2] drm: Replace drm_framebuffer plane size functions with its equivalents
  2023-09-26 14:15 ` [PATCH v2 2/2] drm: Replace drm_framebuffer plane size functions with its equivalents Carlos Eduardo Gallo Filho
@ 2023-10-01  9:10   ` André Almeida
  0 siblings, 0 replies; 5+ messages in thread
From: André Almeida @ 2023-10-01  9:10 UTC (permalink / raw)
  To: Carlos Eduardo Gallo Filho
  Cc: Tvrtko Ursulin, Juha-Pekka Heikkila, Tales Aparecida, intel-gfx,
	Maxime Ripard, Maira Canal, Matt Roper, dri-devel,
	Thomas Zimmermann, Rodrigo Vivi, Niranjana Vishwanathapura,
	Arthur Grillo

On 9/26/23 16:15, Carlos Eduardo Gallo Filho wrote:
> The functions drm_framebuffer_plane_{width,height} and
> fb_plane_{width,height} do exactly the same job of its
> equivalents drm_format_info_plane_{width,height} from drm_fourcc.
>
> The only reason to have these functions on drm_framebuffer
> would be if they would added a abstraction layer to call it just
> passing a drm_framebuffer pointer and the desired plane index,
> which is not the case, where these functions actually implements
> just part of it. In the actual implementation, every call to both
> drm_framebuffer_plane_{width,height} and fb_plane_{width,height} should
> pass some drm_framebuffer attribute, which is the same as calling the
> drm_format_info_plane_{width,height} functions.
>
> The drm_format_info_pane_{width,height} functions are much more
> consistent in both its implementation and its location on code. The
> kind of calculation that they do is intrinsically derivated from the
> drm_format_info struct and has not to do with drm_framebuffer, except
> by the potential motivation described above, which is still not a good
> justification to have drm_framebuffer functions to calculate it.
>
> So, replace each drm_framebuffer_plane_{width,height} and
> fb_plane_{width,height} call to drm_format_info_plane_{width,height}
> and remove them.
>
> Signed-off-by: Carlos Eduardo Gallo Filho <gcarlos@disroot.org>
Reviewed-by: André Almeida <andrealmeid@igalia.com>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 2/2] drm: Replace drm_framebuffer plane size functions with its equivalents
  2023-09-26 14:15 [RESEND PATCH v2 0/2] drm: Refactor plane size calculation by core helper functions Carlos Eduardo Gallo Filho
@ 2023-09-26 14:15 ` Carlos Eduardo Gallo Filho
  2023-10-01  9:10   ` André Almeida
  0 siblings, 1 reply; 5+ messages in thread
From: Carlos Eduardo Gallo Filho @ 2023-09-26 14:15 UTC (permalink / raw)
  To: intel-gfx, dri-devel
  Cc: Matt Roper, Tvrtko Ursulin, André Almeida,
	Juha-Pekka Heikkila, Tales Aparecida, Carlos Eduardo Gallo Filho,
	Maxime Ripard, Maira Canal, Thomas Zimmermann, Rodrigo Vivi,
	Niranjana Vishwanathapura, Arthur Grillo

The functions drm_framebuffer_plane_{width,height} and
fb_plane_{width,height} do exactly the same job of its
equivalents drm_format_info_plane_{width,height} from drm_fourcc.

The only reason to have these functions on drm_framebuffer
would be if they would added a abstraction layer to call it just
passing a drm_framebuffer pointer and the desired plane index,
which is not the case, where these functions actually implements
just part of it. In the actual implementation, every call to both
drm_framebuffer_plane_{width,height} and fb_plane_{width,height} should
pass some drm_framebuffer attribute, which is the same as calling the
drm_format_info_plane_{width,height} functions.

The drm_format_info_pane_{width,height} functions are much more
consistent in both its implementation and its location on code. The
kind of calculation that they do is intrinsically derivated from the
drm_format_info struct and has not to do with drm_framebuffer, except
by the potential motivation described above, which is still not a good
justification to have drm_framebuffer functions to calculate it.

So, replace each drm_framebuffer_plane_{width,height} and
fb_plane_{width,height} call to drm_format_info_plane_{width,height}
and remove them.

Signed-off-by: Carlos Eduardo Gallo Filho <gcarlos@disroot.org>
---
 drivers/gpu/drm/drm_framebuffer.c       | 64 ++-----------------------
 drivers/gpu/drm/i915/display/intel_fb.c |  2 +-
 include/drm/drm_framebuffer.h           |  5 --
 3 files changed, 5 insertions(+), 66 deletions(-)

diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index ba51deb6d042..d3ba0698b84b 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -151,24 +151,6 @@ int drm_mode_addfb_ioctl(struct drm_device *dev,
 	return drm_mode_addfb(dev, data, file_priv);
 }
 
-static int fb_plane_width(int width,
-			  const struct drm_format_info *format, int plane)
-{
-	if (plane == 0)
-		return width;
-
-	return DIV_ROUND_UP(width, format->hsub);
-}
-
-static int fb_plane_height(int height,
-			   const struct drm_format_info *format, int plane)
-{
-	if (plane == 0)
-		return height;
-
-	return DIV_ROUND_UP(height, format->vsub);
-}
-
 static int framebuffer_check(struct drm_device *dev,
 			     const struct drm_mode_fb_cmd2 *r)
 {
@@ -196,8 +178,8 @@ static int framebuffer_check(struct drm_device *dev,
 	info = drm_get_format_info(dev, r);
 
 	for (i = 0; i < info->num_planes; i++) {
-		unsigned int width = fb_plane_width(r->width, info, i);
-		unsigned int height = fb_plane_height(r->height, info, i);
+		unsigned int width = drm_format_info_plane_width(info, r->width, i);
+		unsigned int height = drm_format_info_plane_height(info, r->height, i);
 		unsigned int block_size = info->char_per_block[i];
 		u64 min_pitch = drm_format_info_min_pitch(info, i, width);
 
@@ -1136,44 +1118,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);
 
-/**
- * drm_framebuffer_plane_width - width of the plane given the first plane
- * @width: width of the first plane
- * @fb: the framebuffer
- * @plane: plane index
- *
- * Returns:
- * The width of @plane, given that the width of the first plane is @width.
- */
-int drm_framebuffer_plane_width(int width,
-				const struct drm_framebuffer *fb, int plane)
-{
-	if (plane >= fb->format->num_planes)
-		return 0;
-
-	return fb_plane_width(width, fb->format, plane);
-}
-EXPORT_SYMBOL(drm_framebuffer_plane_width);
-
-/**
- * drm_framebuffer_plane_height - height of the plane given the first plane
- * @height: height of the first plane
- * @fb: the framebuffer
- * @plane: plane index
- *
- * Returns:
- * The height of @plane, given that the height of the first plane is @height.
- */
-int drm_framebuffer_plane_height(int height,
-				 const struct drm_framebuffer *fb, int plane)
-{
-	if (plane >= fb->format->num_planes)
-		return 0;
-
-	return fb_plane_height(height, fb->format, plane);
-}
-EXPORT_SYMBOL(drm_framebuffer_plane_height);
-
 void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
 				const struct drm_framebuffer *fb)
 {
@@ -1189,8 +1133,8 @@ void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
 
 	for (i = 0; i < fb->format->num_planes; i++) {
 		drm_printf_indent(p, indent + 1, "size[%u]=%dx%d\n", i,
-				  drm_framebuffer_plane_width(fb->width, fb, i),
-				  drm_framebuffer_plane_height(fb->height, fb, i));
+				  drm_format_info_plane_width(fb->format, fb->width, i),
+				  drm_format_info_plane_height(fb->format, fb->height, i));
 		drm_printf_indent(p, indent + 1, "pitch[%u]=%u\n", i, fb->pitches[i]);
 		drm_printf_indent(p, indent + 1, "offset[%u]=%u\n", i, fb->offsets[i]);
 		drm_printf_indent(p, indent + 1, "obj[%u]:%s\n", i,
diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c
index 446bbf7986b6..5de2453ff7a3 100644
--- a/drivers/gpu/drm/i915/display/intel_fb.c
+++ b/drivers/gpu/drm/i915/display/intel_fb.c
@@ -1113,7 +1113,7 @@ static int intel_fb_offset_to_xy(int *x, int *y,
 		return -EINVAL;
 	}
 
-	height = drm_framebuffer_plane_height(fb->height, fb, color_plane);
+	height = drm_format_info_plane_height(fb->format, fb->height, color_plane);
 	height = ALIGN(height, intel_tile_height(fb, color_plane));
 
 	/* Catch potential overflows early */
diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h
index 0dcc07b68654..80ece7b6dd9b 100644
--- a/include/drm/drm_framebuffer.h
+++ b/include/drm/drm_framebuffer.h
@@ -292,11 +292,6 @@ static inline void drm_framebuffer_assign(struct drm_framebuffer **p,
 	     &fb->head != (&(dev)->mode_config.fb_list);			\
 	     fb = list_next_entry(fb, head))
 
-int drm_framebuffer_plane_width(int width,
-				const struct drm_framebuffer *fb, int plane);
-int drm_framebuffer_plane_height(int height,
-				 const struct drm_framebuffer *fb, int plane);
-
 /**
  * struct drm_afbc_framebuffer - a special afbc frame buffer object
  *
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-10-01  9:11 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-20  2:19 [PATCH v2 0/2] drm: Refactor plane size calculation by core helper functions Carlos Eduardo Gallo Filho
2023-07-20  2:19 ` [PATCH v2 1/2] drm: Remove plane hsub/vsub alignment requirement for core helpers Carlos Eduardo Gallo Filho
2023-07-20  2:19 ` [PATCH v2 2/2] drm: Replace drm_framebuffer plane size functions with its equivalents Carlos Eduardo Gallo Filho
2023-09-26 14:15 [RESEND PATCH v2 0/2] drm: Refactor plane size calculation by core helper functions Carlos Eduardo Gallo Filho
2023-09-26 14:15 ` [PATCH v2 2/2] drm: Replace drm_framebuffer plane size functions with its equivalents Carlos Eduardo Gallo Filho
2023-10-01  9:10   ` André Almeida

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).