Commit 1ec0b899 authored by Sakari Ailus's avatar Sakari Ailus Committed by Mauro Carvalho Chehab
Browse files

media: ccs: Add the generator for CCS register definitions and limits



Add register definitions of the MIPI CCS 1.1 standard.

The CCS driver makes extended use of device's capability registers that
are dependent on CCS version. This involves having an in-memory data
structure for limit and capability information, creating that data
structure and accessing it.

The register definitions as well as the definitions of this data structure
are generated from a text file using a Perl script. Add the generator
script to make it easy to update the generated files.

Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 67e061f0
Loading
Loading
Loading
Loading
+1041 −0

File added.

Preview size limit exceeded, changes collapsed.

+433 −0
Original line number Diff line number Diff line
#!/usr/bin/perl -w
# SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
# Copyright (C) 2019--2020 Intel Corporation

use Getopt::Long qw(:config no_ignore_case);
use File::Basename;

my $ccsregs = "ccs-regs.txt";
my $header;
my $regarray;
my $limitc;
my $limith;
my $kernel;
my $help;

GetOptions("ccsregs|c=s" => \$ccsregs,
	   "header|e=s" => \$header,
	   "regarray|r=s" => \$regarray,
	   "limitc|l=s" => \$limitc,
	   "limith|L=s" => \$limith,
	   "kernel|k" => \$kernel,
	   "help|h" => \$help) or die "can't parse options";

$help = 1 if ! defined $header || ! defined $limitc || ! defined $limith;

if (defined $help) {
	print <<EOH
$0 - Create CCS register definitions for C

usage: $0 -c ccs-regs.txt -e header -r regarray -l limit-c -L limit-header [-k]

	-c ccs register file
	-e header file name
	-r register description array file name
	-l limit and capability array file name
	-L limit and capability header file name
	-k generate files for kernel space consumption
EOH
	  ;
	exit 0;
}

my $lh_hdr = ! defined $kernel
	? '#include "ccs-os.h"' . "\n"
	: "#include <linux/bits.h>\n#include <linux/types.h>\n";
my $uint32_t = ! defined $kernel ? 'uint32_t' : 'u32';
my $uint16_t = ! defined $kernel ? 'uint16_t' : 'u16';

open(my $R, "< $ccsregs") or die "can't open $ccsregs";

open(my $H, "> $header") or die "can't open $header";
my $A;
if (defined $regarray) {
	open($A, "> $regarray") or die "can't open $regarray";
}
open(my $LC, "> $limitc") or die "can't open $limitc";
open(my $LH, "> $limith") or die "can't open $limith";

my %this;

sub is_limit_reg($) {
	my $addr = hex $_[0];

	return 0 if $addr < 0x40; # weed out status registers
	return 0 if $addr >= 0x100 && $addr < 0xfff; # weed out configuration registers

	return 1;
}

my $uc_header = basename uc $header;
$uc_header =~ s/[^A-Z0-9]/_/g;

my $copyright = "/* Copyright (C) 2019--2020 Intel Corporation */\n";
my $license = "SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause";

for my $fh ($A, $LC) {
	print $fh "// $license\n$copyright\n" if defined $fh;
}

for my $fh ($H, $LH) {
	print $fh "/* $license */\n$copyright\n";
}

sub bit_def($) {
	my $bit = shift @_;

	return "BIT($bit)" if defined $kernel;
	return "(1U << $bit)" if $bit =~ /^[a-zA-Z0-9_]+$/;
	return "(1U << ($bit))";
}

print $H <<EOF
#ifndef __${uc_header}__
#define __${uc_header}__

EOF
  ;

print $H "#include <linux/bits.h>\n\n" if defined $kernel;

print $H <<EOF
#define CCS_FL_BASE		16
EOF
  ;

print $H "#define CCS_FL_16BIT		" . bit_def("CCS_FL_BASE") . "\n";
print $H "#define CCS_FL_32BIT		" . bit_def("CCS_FL_BASE + 1") . "\n";
print $H "#define CCS_FL_FLOAT_IREAL	" . bit_def("CCS_FL_BASE + 2") . "\n";
print $H "#define CCS_FL_IREAL		" . bit_def("CCS_FL_BASE + 3") . "\n";

print $H <<EOF
#define CCS_R_ADDR(r)		((r) & 0xffff)

EOF
  ;

print $A <<EOF
#include <stdint.h>
#include <stdio.h>
#include "ccs-extra.h"
#include "ccs-regs.h"

EOF
	if defined $A;

my $uc_limith = basename uc $limith;
$uc_limith =~ s/[^A-Z0-9]/_/g;

print $LH <<EOF
#ifndef __${uc_limith}__
#define __${uc_limith}__

$lh_hdr
struct ccs_limit {
	$uint32_t reg;
	$uint16_t size;
	$uint16_t flags;
	const char *name;
};

EOF
  ;
print $LH "#define CCS_L_FL_SAME_REG	" . bit_def(0) . "\n\n";

print $LH <<EOF
extern const struct ccs_limit ccs_limits[];

EOF
  ;

print $LC <<EOF
#include "ccs-limits.h"
#include "ccs-regs.h"

const struct ccs_limit ccs_limits[] = {
EOF
  ;

my $limitcount = 0;
my $argdescs;
my $reglist = "const struct ccs_reg_desc ccs_reg_desc[] = {\n";

sub name_split($$) {
	my ($name, $addr) = @_;
	my $args;

	$name =~ /([^\(]+?)(\(.*)/;
	($name, $args) = ($1, $2);
	$args = [split /,\s*/, $args];
	foreach my $t (@$args) {
		$t =~ s/[\(\)]//g;
		$t =~ s/\//\\\//g;
	}

	return ($name, $addr, $args);
}

sub tabconv($) {
	$_ = shift;

	my @l = split "\n", $_;

	map {
		s/ {8,8}/\t/g;
		s/\t\K +//;
	} @l;

	return (join "\n", @l) . "\n";
}

sub elem_size(@) {
	my @flags = @_;

	return 2 if grep /^16$/, @flags;
	return 4 if grep /^32$/, @flags;
	return 1;
}

sub arr_size($) {
	my $this = $_[0];
	my $size = $this->{elsize};
	my $h = $this->{argparams};

	foreach my $arg (@{$this->{args}}) {
		my $apref = $h->{$arg};

		$size *= $apref->{max} - $apref->{min} + 1;
	}

	return $size;
}

sub print_args($$$) {
	my ($this, $postfix, $is_same_reg) = @_;
	my ($args, $argparams, $name) =
	  ($this->{args}, $this->{argparams}, $this->{name});
	my $varname = "ccs_reg_arg_" . (lc $name) . $postfix;
	my @mins;
	my @sorted_args = @{$this->{sorted_args}};
	my $lim_arg;
	my $size = arr_size($this);

	$argdescs .= "static const struct ccs_reg_arg " . $varname . "[] = {\n";

	foreach my $sorted_arg (@sorted_args) {
		push @mins, $argparams->{$sorted_arg}->{min};
	}

	foreach my $sorted_arg (@sorted_args) {
		my $h = $argparams->{$sorted_arg};

		$argdescs .= "\t{ \"$sorted_arg\", $h->{min}, $h->{max}, $h->{elsize} },\n";

		$lim_arg .= defined $lim_arg ? ", $h->{min}" : "$h->{min}";
	}

	$argdescs .= "};\n\n";

	$reglist .= "\t{ CCS_R_" . (uc $name) . "(" . (join ",", (@mins)) .
	  "), $size, sizeof($varname) / sizeof(*$varname)," .
	    " \"" . (lc $name) . "\", $varname },\n";

	print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . "($lim_arg), " .
	  $size . ", " . ($is_same_reg ? "CCS_L_FL_SAME_REG" : "0") .
	    ", \"$name" . (defined $this->{discontig} ? " $lim_arg" : "") . "\" },\n"
	      if is_limit_reg $this->{base_addr};
}

my $hdr_data;

while (<$R>) {
	chop;
	s/^\s*//;
	next if /^[#;]/ || /^$/;
	if (s/^-\s*//) {
		if (s/^b\s*//) {
			my ($bit, $addr) = split /\t+/;
			$bit = uc $bit;
			$hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) ."_$bit", bit_def($addr) . "\n";
		} elsif (s/^f\s*//) {
			s/[,\.-]/_/g;
			my @a = split /\s+/;
			my ($msb, $lsb, $this_field) = reverse @a;
		        @a = ( { "name" => "SHIFT", "addr" => $lsb, "fmt" => "%uU", },
			       { "name" => "MASK", "addr" => (1 << ($msb + 1)) - 1 - ((1 << $lsb) - 1), "fmt" => "0x%" . join(".", ($this{"elsize"} >> 2) x 2) . "x" } );
			$this{"field"} = $this_field;
			foreach my $ar (@a) {
				#print $ar->{fmt}."\n";
				$hdr_data .= sprintf "#define %-62s " . $ar->{"fmt"} . "\n", "CCS_" . (uc $this{"name"}) . (defined $this_field ? "_" . uc $this_field : "") . "_" . $ar->{"name"}, $ar->{"addr"} . "\n";
			}
		} elsif (s/^e\s*//) {
			s/[,\.-]/_/g;
			my ($enum, $addr) = split /\s+/;
			$enum = uc $enum;
			$hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) . (defined $this{"field"} ? "_" . uc $this{"field"} : "") ."_$enum", $addr . ($addr =~ /0x/i ? "" : "U") . "\n";
		} elsif (s/^l\s*//) {
			my ($arg, $min, $max, $elsize, @discontig) = split /\s+/;
			my $size;

			foreach my $num ($min, $max) {
				$num = hex $num if $num =~ /0x/i;
			}

			$hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MIN_$arg"), $min . ($min =~ /0x/i ? "" : "U") . "\n";
			$hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MAX_$arg"), $max . ($max =~ /0x/i ? "" : "U") . "\n";

			my $h = $this{argparams};

			$h->{$arg} = { "min" => $min,
				       "max" => $max,
				       "elsize" => $elsize =~ /^0x/ ? hex $elsize : $elsize,
				       "discontig" => \@discontig };

			$this{discontig} = $arg if @discontig;

			next if $#{$this{args}} + 1 != scalar keys %{$this{argparams}};

			my $reg_formula = "($this{addr}";
			my $lim_formula;

			foreach my $arg (@{$this{args}}) {
				my $d = $h->{$arg}->{discontig};
				my $times = $h->{$arg}->{elsize} != 1 ?
				  " * " . $h->{$arg}->{elsize} : "";

				if (@$d) {
					my ($lim, $offset) = split /,/, $d->[0];

					$reg_formula .= " + (($arg) < $lim ? ($arg)$times : $offset + (($arg) - $lim)$times)";
				} else {
					$reg_formula .= " + ($arg)$times";
				}

				$lim_formula .= (defined $lim_formula ? " + " : "") . "($arg)$times";
			}

			$reg_formula .= ")\n";
			$lim_formula =~ s/^\(([a-z0-9]+)\)$/$1/i;

			print $H tabconv sprintf("#define %-62s %s", "CCS_R_" . (uc $this{name}) .
			  $this{arglist}, $reg_formula);

			print $H tabconv $hdr_data;
			undef $hdr_data;

			# Sort arguments in descending order by size
			@{$this{sorted_args}} = sort {
				$h->{$a}->{elsize} <= $h->{$b}->{elsize}
			} @{$this{args}};

			if (defined $this{discontig}) {
				my $da = $this{argparams}->{$this{discontig}};
				my ($first_discontig) = split /,/, $da->{discontig}->[0];
				my $max = $da->{max};

				$da->{max} = $first_discontig - 1;
				print_args(\%this, "", 0);

				$da->{min} = $da->{max} + 1;
				$da->{max} = $max;
				print_args(\%this, $first_discontig, 1);
			} else {
				print_args(\%this, "", 0);
			}

			next unless is_limit_reg $this{base_addr};

			print $LH tabconv sprintf "#define %-63s%s\n",
			  "CCS_L_" . (uc $this{name}) . "_OFFSET(" .
			    (join ", ", @{$this{args}}) . ")", "($lim_formula)";
		}

		if (! @{$this{args}}) {
			print $H tabconv($hdr_data);
			undef $hdr_data;
		}

		next;
	}

	my ($name, $addr, @flags) = split /\t+/, $_;
	my $args = [];

	my $sp;

	($name, $addr, $args) = name_split($name, $addr) if /\(.*\)/;

	$name =~ s/[,\.-]/_/g;

	my $flagstring = "";
	my $size = elem_size(@flags);
	$flagstring .= "| CCS_FL_16BIT " if $size eq "2";
	$flagstring .= "| CCS_FL_32BIT " if $size eq "4";
	$flagstring .= "| CCS_FL_FLOAT_IREAL " if grep /^float_ireal$/, @flags;
	$flagstring .= "| CCS_FL_IREAL " if grep /^ireal$/, @flags;
	$flagstring =~ s/^\| //;
	$flagstring =~ s/ $//;
	$flagstring = "($flagstring)" if $flagstring =~ /\|/;
	my $base_addr = $addr;
	$addr = "($addr | $flagstring)" if $flagstring ne "";

	my $arglist = @$args ? "(" . (join ", ", @$args) . ")" : "";
	$hdr_data .= sprintf "#define %-62s %s\n", "CCS_R_" . (uc $name), $addr
	  if !@$args;

	$name =~ s/\(.*//;

	%this = ( name => $name,
		  addr => $addr,
		  base_addr => $base_addr,
		  argparams => {},
		  args => $args,
		  arglist => $arglist,
		  elsize => $size,
		);

	if (!@$args) {
		$reglist .= "\t{ CCS_R_" . (uc $name) . ", 1,  0, \"" . (lc $name) . "\", NULL },\n";
		print $H tabconv $hdr_data;
		undef $hdr_data;

		print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . ", " .
		  $this{elsize} . ", 0, \"$name\" },\n"
		    if is_limit_reg $this{base_addr};
	}

	print $LH tabconv sprintf "#define %-63s%s\n",
	  "CCS_L_" . (uc $this{name}), $limitcount++
	    if is_limit_reg $this{base_addr};
}

if (defined $A) {
	print $A $argdescs, $reglist;

	print $A "\t{ 0 }\n";

	print $A "};\n";
}

print $H "\n#endif /* __${uc_header}__ */\n";

print $LH tabconv sprintf "#define %-63s%s\n", "CCS_L_LAST", $limitcount;

print $LH "\n#endif /* __${uc_limith}__ */\n";

print $LC "\t{ 0 } /* Guardian */\n";
print $LC "};\n";

close($R);
close($H);
close($A) if defined $A;
close($LC);
close($LH);
+1 −0
Original line number Diff line number Diff line
@@ -16108,6 +16108,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com>
L:	linux-media@vger.kernel.org
S:	Maintained
F:	Documentation/devicetree/bindings/media/i2c/nokia,smia.txt
F:	Documentation/driver-api/media/drivers/ccs/
F:	drivers/media/i2c/smiapp-pll.c
F:	drivers/media/i2c/smiapp-pll.h
F:	drivers/media/i2c/smiapp/